最左前缀原则源于b+树物理结构而非mysql规定;联合索引(a,b,c)中,查询必须从a开始连续等值匹配,中间出现范围或缺失则右侧列失效,order by同理需严格对齐索引顺序。

最左前缀不是MySQL的“规定”,而是B+树的物理事实
联合索引 (a, b, c) 在 InnoDB 中就是一棵 B+ 树,它的叶子节点按 a 全局排序;a 相同的记录,再按 b 排序;a 和 b 都相同的,才按 c 排序。这不是优化器“选择”怎么用索引,而是数据在磁盘上就长这样——跳过 a 直接找 b = 5,就像翻电话簿不看姓氏直接找“小明”,数据库只能从头扫到尾。
WHERE a = 1 AND c = 3 只能用上 a 列,c 列不会出现在 key_len 中
执行 EXPLAIN 时会发现 key_len 值只反映 a 的字节数,key 字段显示实际使用的索引名,但 Extra 里常带 Using where,说明 c = 3 是在索引扫描后靠 MySQL Server 层逐行过滤的(ICP 也救不了,因为 b 缺失导致 c 在索引中无序)。这种查询本质是“索引范围扫描 + 回表后过滤”,性能远低于全匹配。
-
WHERE a = 1 AND b = 2 AND c = 3→key_len包含全部三列长度 -
WHERE a = 1 AND c = 3→key_len仅含a,c不参与定位 -
WHERE b = 2 AND c = 3→key为NULL,退化为全表扫描
范围查询(>、
比如索引 (status, create_time, user_id),WHERE status = 'done' AND create_time > '2025-01-01' 能用上前两列,但 user_id 就断了——因为 create_time > ... 导致对应子树内 user_id 是乱序的,B+ 树无法继续二分查找。这个中断是刚性的,和值分布、统计信息无关。
-
WHERE status = 'done' AND create_time = '2025-01-01' AND user_id = 100→ 三列全走索引 -
WHERE status = 'done' AND user_id = 100 AND create_time = '2025-01-01'→ 条件顺序不影响,仍全走(优化器会重排) -
WHERE status > 'done' AND create_time = '2025-01-01'→ 仅status生效,create_time失效
ORDER BY 也受最左前缀约束,顺序错位就触发 filesort
索引 (a, b, c) 可以加速 ORDER BY a, b 或 ORDER BY a, b, c,但 ORDER BY b, c 或 ORDER BY a, c 都无法利用索引有序性。执行计划里一旦出现 Using filesort,基本说明排序字段没对齐索引定义顺序。
-
SELECT * FROM t WHERE a = 1 ORDER BY b, c→b, c在a = 1子集内是有序的,可免排序 -
SELECT * FROM t WHERE a > 1 ORDER BY b, c→a > 1对应多个不连续子树,b, c整体无序,必 filesort -
SELECT * FROM t ORDER BY b, a→ 完全不匹配索引顺序,必 filesort
真正容易被忽略的是:最左前缀生效与否,不取决于 SQL 写法顺序,而取决于索引定义顺序 + 查询条件是否形成连续等值链,中间任何一环断裂(缺列、范围、不等号),右侧列就彻底出局。别寄希望于优化器“聪明”,它只是忠实地按 B+ 树结构干活。











