bigywater.tech
MySQL / InnoDB · 11

联合索引的最左匹配:为什么只查第二列会失效

建一张10万行的表,联合索引idx(a,b,c),用EXPLAIN对比只过滤b、只过滤a、过滤a+b、过滤a+b+c这四种情况,看最左匹配的生效方式。

跳过a即失效
只过滤b要扫描全部索引
命中a即生效
从最左列开始一段段用上索引
1000行 vs 10万行
命中最左列与否的扫描量差距

做的事:一张10万行的表,一个三列联合索引

建表 idx_abc(a,b,c),插入10万行,用4种不同的WHERE组合测EXPLAIN。

建表插入CREATE TABLE t2 (id INT PRIMARY KEY AUTO_INCREMENT, a INT, b INT, c INT, INDEX idx_abc(a,b,c)); 插入10万行:a=i%100, b=i%50, c=i%20

联合索引 idx_abc(a,b,c) 是按 a 排第一优先级、a相同再按b排、a和b都相同再按c排的一棵树——不是三个独立的索引拼在一起。

验证:只过滤第二列b,索引完全用不上

WHERE b=10,b不是联合索引的第一列。

EXPLAINEXPLAIN SELECT * FROM t2 WHERE b=10 → Filter: (t2.b = 10) (cost=10000 rows=10000) → Covering index scan on t2 using idx_abc (cost=10000 rows=100000)

的执行计划是"扫描整个 idx_abc 索引(10万行),再逐行过滤 b=10"——虽然扫的是索引不是表,但要把10万行全部过一遍,跟没走索引条件过滤差别不大。原因很直接:这棵树是先按a排序的,a取值杂乱分布时,符合b=10的记录会散落在整棵树的各个角落,索引没法帮你跳过任何一段连续区间。

验证:从只过滤a,到a+b,到a+b+c

测三种递增的过滤组合。

EXPLAIN结果WHERE a=10 → Covering index lookup on t2 using idx_abc (a = 10) (cost=101 rows=1000) WHERE a=10 AND b=10 → Covering index lookup on t2 using idx_abc (a = 10, b = 10) (cost=101 rows=1000) WHERE a=10 AND b=10 AND c=10 → Covering index lookup on t2 using idx_abc (a = 10, b = 10, c = 10) (cost=101 rows=1000)

只要WHERE条件从a开始(最左列),索引立刻就能用:"Covering index lookup"里括号中列出的条件,就是被索引利用到的部分。注意这里cost和rows估计值三次都一样(因为本文的数据分布下a本身已经把范围锁定到1000行,再加b、c并没有帮它缩小估计范围),但括号里能看到 (a = 10, b = 10, c = 10) 三个条件都被索引直接利用了,不需要事后再筛选。

这就是"最左匹配"的含义:联合索引只能从第一列开始,按顺序一段一段地用——用到a,可以继续用到b;用到a和b,可以继续用到c;但如果第一列没出现在条件里(比如只有b或只有c),索引直接失去连续区间可跳的能力,退化成扫全部索引再逐行过滤。

分步演示:最左匹配是怎么一段段生效的

每一步都是本文上面用EXPLAIN得到的原始结果。

联合索引是从最左列开始一段段生效的
这一步新验证的过滤条件

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),10万行三列联合索引表
    • EXPLAIN:对比不同过滤组合下的索引利用情况
    京ICP备2026067502号-1京公网安备11010502063487号