小型电商项目应优先优化mysql单实例索引与设计:orders表需user_id、(status,created_at)索引,products表需category_id、status、sku唯一索引;禁用函数操作导致索引失效;库存扣减用where stock>=1校验;订单状态用tinyint+应用层常量管理;开发环境对齐配置并批量导入万级测试数据。

小型电商项目不需要一开始就上分布式或分库分表,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 时隐式转换会悄悄干掉索引。










