因为联合索引遵循最左匹配原则,status和created_at顺序错则索引失效:等值查询字段(如status)必须在前,范围/排序字段(如created_at)在后,才能支持where+order by高效执行。

为什么 status 和 created_at 不能随便组合建索引?
因为查询模式决定索引有效性。电商订单常见查询是「查某状态(如 'paid')下最近 N 条订单」或「查某时间段内某状态的订单」,这两种场景对字段顺序极其敏感。如果建 INDEX(status, created_at),能高效支持 WHERE status = 'paid' ORDER BY created_at DESC LIMIT 20;但反过来建 INDEX(created_at, status),对纯状态过滤就几乎无效——MySQL 无法跳过范围扫描 created_at 直接定位 status 值。
实际建索引时怎么选字段顺序?
看 WHERE 条件中哪个字段过滤性更强、是否含等值判断:
- 等值 + 范围:优先等值字段在前,比如
WHERE status = 'shipped' AND created_at > '2024-01-01'→ 用(status, created_at) - 多个等值:把区分度高的放前面,
status通常只有 5–10 个枚举值,区分度低;但如果加一个shop_id(百万级),就该是(shop_id, status, created_at) - 仅时间范围:单独查「最近7天订单」不带状态,那
created_at单独索引更合适,组合索引帮不上忙
ORDER BY 和 LIMIT 对组合索引的影响
MySQL 利用索引排序的前提是:ORDER BY 字段必须紧跟在 WHERE 等值条件之后,且方向一致。例如:
SELECT * FROM orders WHERE status = 'paid' ORDER BY created_at DESC LIMIT 10;
对应索引 (status, created_at) 可全程走索引,无需回表排序;但如果写成 ORDER BY created_at ASC,而索引是 (status, created_at DESC)(显式指定降序),部分老版本 MySQL 会失效。稳妥做法是保持默认升序,业务层适配 DESC 查询逻辑。
另外注意:LIMIT 越大,索引覆盖越关键。若只查 ID 和状态,加 INCLUDE(8.0+)或扩展为覆盖索引更省 IO。
上线前必须验证的三件事
别只看 EXPLAIN 显示 type=ref 就放心:
- 用
EXPLAIN FORMAT=JSON看used_key_parts是否完整命中组合索引所有字段 - 检查
rows预估是否明显大于实际匹配数(说明索引选择不佳或统计信息过期) - 在生产流量低峰期用
pt-query-digest抽样慢日志,确认真实执行路径和key_len
最常被忽略的是:订单表写入频繁,组合索引越多,INSERT/UPDATE 开销越大。一个字段参与 3 个组合索引,每次更新就要维护 3 次 B+ 树,延迟可能翻倍。











