联合索引最左前缀失效
场景痛点
订单表建了联合索引 (user_id, status, created_at),但后台报表查询时只按 status 和 created_at 过滤,没有传 user_id。50 万行的表查询要 2 秒,明明有联合索引却走全表扫描。
问题分析
sql
-- bad.sql: 跳过了联合索引的最左列 user_id
SELECT id, user_id, order_no, status, amount, created_at
FROM t_order_latest
WHERE status = 1 AND created_at > '2026-01-01';EXPLAIN: type=ALL, key=NULL, rows=300,003 -- 联合索引完全失效。
原因:B+ 树联合索引按 (user_id, status, created_at) 排序。跳过 user_id 后,status 在索引中无序分布,无法二分查找定位。
核心认知
联合索引遵循最左前缀原则:必须从最左列开始连续使用。跳过中间任何一列,后面的列都无法走索引。
优化方案
sql
-- good.sql: 补全最左前缀列 user_id
SELECT id, user_id, order_no, status, amount, created_at
FROM t_order_latest
WHERE user_id = 12345 AND status = 1 AND created_at > '2026-01-01';EXPLAIN: type=range, key=idx_user_status_created, rows=3 -- 三列都走索引。
| 指标 | 优化前 (bad) | 优化后 (good) |
|---|---|---|
| 访问类型 | ALL | range |
| 使用索引 | NULL (索引失效) | idx_user_status_created |
| 扫描行数 | 300,003 | 3 |
| 附加信息 | Using where | Using index condition |
🚀 扫描行数下降 99.999%,30万行 -> 3行
避坑指南
注意事项
- 最左前缀不可跳过:
(a,b,c)索引,查b和c不走索引,必须从a开始。 - 中间列不能跳过:查
a和c(跳过b),只有a走索引,c不走。 - 等值条件可乱序:
WHERE b=1 AND a=2优化器自动调整为(a,b)顺序。 - 范围查询截断后续列:详见 案例 07。
本地复现
bash
./scripts/run-case.sh 02-leftmost-prefix