题目:MySQL索引一定遵循最左前缀匹配吗?
难度:medium
标签:db/mysql/index, mysql/index, difficulty/medium
评分:⭐⭐⭐⭐☆(4/5)
这个前缀匹配是 MySQL 基础的索引中使用的基础规则,但并不是一定遵守,因为它还有包括全文索引、位图索引,还有其他的索引方式。
比如全文索引,它创建的索引就是对于索引字段进行分词,然后比对,和最左前缀没什么关系。
最左前缀只是指包括联合索引和普通的索引中使用的索引规则。其实主要是联合索引,普通索引直接比对字段了,和最左前缀没什么关系。然后 Like 的时候使用的也类似于最左前缀,因为它比对是从左往右。如果是中间或者是右侧区域 Like,在左侧 Like 的时候,使用模糊匹配,它是不能走索引的,这也是最左前缀的一个体现。
全文索引就不是这样了
优点:
LIKE %xx 走不了索引、LIKE xx% 可以,说明理解了 B+ 树有序性不足:
OR 条件可能走多个单列索引再合并,绕过单个联合索引的最左前缀INDEX((lower(name))),绕过"函数导致索引失效"补充:
Extra=Using index conditionOR 条件多索引合并,Extra=Using union(...)Extra=Using index conditionWITH PARSER ngram 支持中文核心考点定位:理解最左前缀规则的底层原因(B+ 树有序性),以及 MySQL 8.0 引入的多个"绕开"机制。
知识点拆解:
(a, b, c) 索引内部:先按 a 排,a 相同时按 b 排,a、b 都相同时按 c 排(gender, city) 索引,WHERE city = 'Beijing' 会被拆成 gender='M' AND city='Beijing' 和 gender='F' AND city='Beijing' 两次扫描合并。EXPLAIN 显示 Using index conditionWHERE a=1 OR b=2 可能走 (a) 和 (b) 两个索引再 UNION,EXPLAIN 显示 Using union(idx_a, idx_b)INDEX((lower(name))) 让 WHERE lower(name)='x' 也能走索引LIKE 'xx%':前缀确定,走 B+ 树范围扫描LIKE '%xx' / LIKE '%xx%':前缀不确定,无法利用 B+ 树有序性 → 全表扫描LIKE 'x_x%':中间通配符会中断,但仍比 %xx 高效(有前缀 x)(a, b, c) 索引,WHERE a=1 AND c=3(c 不是最左前缀)EXPLAIN 里看 key=None + type=ALL 就是优化器放弃索引FORCE INDEX / IGNORE INDEX 可以强制干预,但线上慎用(索引被删直接生产报错)= 和 IN 精确匹配考察方向:
延伸递进面试题:
EXPLAIN 显示 Using index condition。验证:EXPLAIN FORMAT=JSON 看 plan_info 里的 skip_scan 字段(a, b, c) 索引,WHERE a=1 AND c=3 会不会因为 c 不满足最左前缀而全表扫描?(a) 的索引,然后 c=3 通过 ICP 下推到存储引擎层过滤,减少回表。EXPLAIN Extra 显示 Using index conditionFORCE INDEX(idx_xxx) 或 USE INDEX。风险:索引被删或改结构后 SQL 直接生产报错;索引统计信息更新后成本变化可能不如全表快;无法动态调整。生产慎用面试官您好,最左前缀不是一定遵守的,我按"底层原理 + 绕开机制"两层来讲。
底层原理:最左前缀本质是 B+ 树的有序性决定的。(a, b, c) 联合索引在 B+ 树里是先按 a 排,a 相同再按 b 排,所以必须先固定前面的列才能利用后面的列的有序区间定位。跳过中间列 → 后面的列在这个前缀下无序 → 索引用不上。这个规则只适用于 B+ 树类索引,所以索引类型本身就会打破它。
能绕开最左前缀的机制,MySQL 8.0 已经比较多了。第一是 Skip Scan 跳跃扫描,8.0 新特性,当首列基数很低(比如性别只有两个值),且查询在后列也能精确匹配时,优化器会跳过首列做一次遍历合并结果——WHERE city='Beijing' 在 (gender, city) 索引上就能跳过 gender 直接扫 city。第二是 Index Merge,WHERE a=1 OR b=2 走两个单列索引再 UNION,绕开单个联合索引。第三是 Functional Index,8.0 可以建表达式索引,INDEX((lower(name))) 让 WHERE lower(name)='x' 也能走。第四是其他索引类型,比如全文索引是倒排结构、Hash 索引是哈希定位,跟 B+ 树最左前缀规则完全无关。第五是优化器成本模型——即使满足最左前缀,如果估算扫描行数占比太高,优化器也可能直接放弃走全表。
再补充一个容易被误会的点:ICP(索引条件下推)不是绕开最左前缀,而是让"看起来用不上最左前缀"的列也在索引层过滤。(a, b, c) 索引上 WHERE a=1 AND c=3,c 虽然不在最左前缀上,但通过 ICP 下推到存储引擎层过滤,能显著减少回表次数。
至于优化器放弃索引的场景,MySQL 5.7 可以 FORCE INDEX 强制走,但生产环境慎用——索引被删或改结构 SQL 直接生产报错,而且统计信息变化后可能反而比全表慢。实际线上更推荐通过调整索引设计让优化器自动选对,而不是硬干预。
当然,最左前缀这个规则在 8.0 里被大量优化器机制"松绑"了,理解它的关键还是回到 B+ 树的有序性——只要索引结构是 B+ 树,规则本质成立;只要索引结构不是 B+ 树,或者优化器判断 B+ 树扫描代价过高,规则就会被打破。