小型电商项目不需要一开始就上分布式或分库分表,MySQL 单实例 + 合理设计就能撑住日活几千的订单和商品查询。关键不是“怎么装 MySQL”,而是“怎么让表不拖垮查询、不锁死写入、不存错数据”。
商品和订单表必须加哪些索引
没索引的
orders表查用户历史订单会全表扫描,10 万行就明显卡顿;
products表没索引时按分类查商品,连后台管理都点不动。
orders表至少要有:
user_id(查某人全部订单)、
status和
created_at联合索引(查待发货/近 7 天订单)
products表必须有:
category_id(前台分类页)、
status(只查上架中商品)、
id主键之外,别忘了
sku字段要加唯一索引——防止重复上架同一 SKU 避免在
WHERE里对字段做函数操作,比如
WHERE DATE(created_at) = '2024-05-01'会让索引失效,改用
WHERE created_at >= '2024-05-01' AND created_at
库存扣减为什么不能只靠 UPDATE ... SET stock = stock - 1
并发下单时,两个请求同时读到 stock=5,各自减 1 写回 4,实际卖了两单却只扣了一次库存——超卖就发生了。
必须用原子操作:UPDATE products SET stock = stock - 1 WHERE id = ? AND stock >= 1,检查影响行数是否为 1 如果影响行为 0,说明库存不足或已被抢完,应用层要立刻返回“库存不足”,不能重试或忽略 别依赖事务隔离级别来防超卖(比如设成
REPEATABLE READ),它解决不了这个场景;真正起作用的是 WHERE 条件里的
stock >= 1这个约束
订单状态变更该用 ENUM 还是 tinyint
ENUM('pending', 'paid', 'shipped', 'completed', 'cancelled') 看着清晰,但上线后加个“refunding”状态就得 ALTER TABLE,锁表时间长,小项目也扛不住。
一律用 TINYINT UNSIGNED,配合应用层定义常量,比如
1 → pending、
2 → paid… 状态流转逻辑必须收口在服务端代码里,数据库只做校验:比如不允许从
completed直接跳回
paid,就在 UPDATE 语句里写死允许的前驱状态:
WHERE status IN (1,2) AND id = ?别把状态名硬编码在 SQL 里,更别用字符串匹配判断状态,
WHERE status = 'shipped'比
WHERE status = 3慢且不可靠
本地开发环境如何避免“上线就慢”
开发机跑得飞快,一上云服务器就慢半拍,大概率是配置没对齐,或者测试数据量级太假。
关掉innodb_flush_log_at_trx_commit = 2(开发可接受崩溃丢一秒日志),否则每条 INSERT 都刷盘,插入 1000 条订单要 10 秒+ 导入测试数据别只插 10 条商品,至少 1 万行;用
INSERT INTO ... VALUES (),(),()...批量插入,别循环单条
INSERT执行慢查询前先
EXPLAIN,重点关注
type是否为
ALL或
index,
key列是否用了预期索引,
rows是否远超实际结果数
最容易被忽略的是字符集——所有表统一用
utf8mb4_unicode_ci,别用
utf8(它不支持 emoji),也别混用 collation,否则 JOIN 时隐式转换会悄悄干掉索引。
