4. 复合索引最左前缀原则的验证与修正
最左前缀原则的验证思路
复合索引 idx_a_b_c(a, b, c) 在 B-tree 中按 a→b→c 的顺序逐层排序。只有当查询条件从最左列 a 开始、且中间列不被跳过时,优化器才能利用索引的有序性做定位或范围扫描。一旦跳过 a 直接过滤 b 或 c,索引的有序性无法被利用,只能退化为全表扫描或全索引扫描。
构造违反最左前缀的查询
假设已有复合索引 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 列无法参与索引查找,只能回表后过滤。这是最左前缀原则在范围查询场景下的延伸表现。