最左前缀匹配原则指联合索引必须从最左列开始连续匹配,遇范围查询(>、<、BETWEEN、LIKE)则后续列仅能ICP过滤;如索引(a,b,c),WHERE a=1 AND c=3仅a生效,跳过a或b则索引失效。

MySQL最左前缀原则不是“要不要用索引”的选择题,而是“索引能不能被用上”的开关——只要查询条件没从联合索引的最左列开始,idx_a_b_c 就基本等于没建。
联合索引 (a, b, c) 实际等价于三个索引
MySQL 并不会为每个组合单独建树,而是靠 B+Tree 的排序结构天然支持前缀匹配。创建 INDEX idx_a_b_c (a, b, c) 后,以下查询能命中索引:
-
WHERE a = 1→ 匹配(a)前缀 -
WHERE a = 1 AND b = 2→ 匹配(a, b)前缀 -
WHERE a = 1 AND b = 2 AND c = 3→ 完全匹配(a, b, c)
关键点在于“连续”和“从左起始”:跳过 a 或中间断开(比如只写 WHERE a = 1 AND c = 3),c 列就无法走索引查找,只能靠索引下推(ICP)在引擎层过滤,但扫描范围仍由 a 决定。
WHERE 条件顺序不影响索引匹配
优化器会自动重排条件,WHERE b = 2 AND a = 1 和 WHERE a = 1 AND b = 2 效果一样,只要逻辑上包含最左列即可。但注意:这不意味着你可以忽略书写习惯——可读性差的 WHERE 容易掩盖漏写最左列的问题,尤其在多人协作或动态拼 SQL 场景下。
- 错误示范:
WHERE status = 'active' AND user_id = 123,而索引是(user_id, status)→ 没问题 - 危险写法:
WHERE status = 'active' AND created_at > '2024-01-01',索引仍是(user_id, status)→status不是最左列,全表扫
范围查询是分水岭,不是终点
WHERE a = 1 AND b > 10 AND c = 5 中,a 和 b 可用于定位和区间扫描,但 c = 5 无法再利用索引做快速查找(B+Tree 在 b > 10 后已失去 c 的有序性),只能靠 ICP 过滤。此时执行计划中 key_len 会显示只用到前两列长度,Extra 出现 Using index condition。
- 常见范围操作符:
>、、<code>BETWEEN、LIKE 'abc%'(注意:LIKE '%abc'会导致索引失效) - 陷阱:
!=或虽不显式写“范围”,但行为等同于全范围排除,也会中断匹配
最容易被忽略的隐式失效点
即使条件写了最左列,也可能因底层转换让索引作废:
- 隐式类型转换:
WHERE a = '123',而a是INT类型 → MySQL 会把字段转成字符串比对,导致无法使用索引 - 函数包裹:
WHERE YEAR(created_at) = 2024→created_at列本身不参与索引查找 - NULL 判断:
WHERE a IS NULL在某些版本/场景下可能不走索引(尤其a无NOT NULL约束时) - OR 条件混用:
WHERE a = 1 OR b = 2,除非b有独立索引,否则大概率放弃联合索引
真正要确认是否生效,别信感觉,直接看 EXPLAIN 输出里的 key 和 key_len 字段——key 为空或不是你预期的索引名,说明最左前缀已经断了。


















