4. 复合索引最左前缀原则的验证与修正

复合索引最左前缀原则的验证与修正 学习卡片

最左前缀原则的验证思路

复合索引 idx_a_b_c(a, b, c) 在 B-tree 中按 a→b→c 的顺序逐层排序。只有当查询条件从最左列 a 开始、且中间列不被跳过时,优化器才能利用索引的有序性做定位或范围扫描。一旦跳过 a 直接过滤 b 或 c,索引的有序性无法被利用,只能退化为全表扫描或全索引扫描。

复合索引 idx_a_b_c(a, b, c) 最左前缀命中判定 B-tree 索引结构(按 a→b→c 逐层排序) a = 10 a = 20 a = 30 … b = 5 b = 8 c = 1 c = 3 查询条件与索引命中判定 查询条件 最左前缀分析 索引使用情况 判定 WHERE a=10 AND b=5 从 a 开始,连续命中 a,b ref 访问,使用 a,b 两列 ✓ 命中 WHERE a=10 AND c=1 a 命中,c 跳过 b 无法定位 ref 访问 a,c 仅作过滤 ✓ 部分命中 WHERE b=5 AND c=1 跳过最左列 a,无法定位 全表扫描 / 全索引扫描 ✗ 失效 WHERE c=1 跳过 a,b,仅 c 无法利用 全表扫描 ✗ 失效 修正方向: ① 补上 a 条件 → WHERE a=10 AND b=5 AND c=1 ② 或调整索引顺序为 (b, c, a)

构造违反最左前缀的查询

假设已有复合索引 idx_a_b_c(a, b, c),分别执行以下查询并观察执行计划:

-- 查询1:命中 a,b(最左连续)
EXPLAIN SELECT * FROM t WHERE a = 10 AND b = 5;

-- 查询2:命中 a,跳过 b 直接过滤 c
EXPLAIN SELECT * FROM t WHERE a = 10 AND c = 1;

-- 查询3:跳过 a,直接过滤 b,c
EXPLAIN SELECT * FROM t WHERE b = 5 AND c = 1;

-- 查询4:仅过滤 c
EXPLAIN SELECT * FROM t WHERE c = 1;

查询1的 key 列显示 idx_a_b_c,type 为 ref,key_len 反映 a、b 两列长度之和。查询2的 key 仍为 idx_a_b_c,但 key_len 只覆盖 a 列,c 列在 Extra 中表现为 Using where 过滤。查询3和查询4的 key 为 NULL,type 为 ALL,即全表扫描。

修正方案与验证

针对查询3 WHERE b=5 AND c=1,有两种修正路径:

  • 补全最左列:如果业务上 a 列有确定值,改写为 WHERE a=10 AND b=5 AND c=1,使索引从 a 开始连续命中。
  • 调整索引顺序:如果 b、c 是高频过滤组合且 a 不常出现在 WHERE 中,可新建索引 idx_b_c_a(b, c, a),让 b 成为最左列。

验证方法:修正后重新执行 EXPLAIN,确认 key 列出现目标索引名,type 从 ALL 变为 ref 或 range,rows 估算值显著下降。同时对比 SHOW STATUS LIKE 'Handler_read%' 中 Handler_read_rnd_next 的变化,确认全表扫描被消除。

注意:范围条件(如 a > 10)之后的列无法继续用于索引定位,只能作为过滤条件。例如 WHERE a > 10 AND b = 5 中,b 列无法参与索引查找,只能回表后过滤。这是最左前缀原则在范围查询场景下的延伸表现。