复合索引index(a,b,c)在where只用col_b时未生效,因mysql遵循最左前缀匹配原则,跳过首列a导致整个索引被忽略;仅当a为常量时b才可命中索引。

WHERE 条件里只用 col_b,INDEX(a, b, c) 为什么没生效?
因为 MySQL 的复合索引严格遵循最左前缀匹配:查询必须从索引第一列开始连续使用,中间不能跳过。只查 col_b,等于跳过了 a,整个索引直接被忽略。
- 验证方法:执行
EXPLAIN SELECT * FROM t WHERE col_b = 1;,看key字段是否为NULL - 例外情况:如果
a是常量(比如WHERE a = 5 AND b = 10),那b就能用上索引 - 注意
ORDER BY和GROUP BY同样受最左前缀约束,不是只有WHERE才算
INDEX(a, b) 和 INDEX(b, a) 能互相替代吗?
不能。列顺序决定索引的物理存储结构和可命中路径,两者在绝大多数场景下不等价。
- 当查询是
WHERE a = ? AND b = ?,两个索引都有效;但WHERE b = ?只能走INDEX(b, a) - 如果还有
ORDER BY a, b,INDEX(a, b)可避免文件排序,而INDEX(b, a)不行 - 联合唯一约束(
UNIQUE(a,b))也依赖顺序:它允许(1,2)和(1,3)共存,但不允许两个(1,2)
把高频过滤字段放最左就一定对?
不一定。高频 ≠ 高区分度,真正影响选择率的是值的分布,不是查询次数。
- 比如
status只有 3 个值('active', 'inactive', 'pending'),即使查得最多,也不该放最左——它会导致索引碎片多、范围扫描大 - 更优策略:把高区分度字段(如
user_id、create_time)放左边,低区分度字段(status、type)靠右,再配合WHERE中的常量条件“激活”右侧列 - 一个典型反例:
INDEX(status, create_time)对WHERE status = 'active' ORDER BY create_time DESC有效;但换成INDEX(create_time, status),ORDER BY能用,WHERE status = ...却失效了
如何用 EXPLAIN FORMAT=JSON 看清最左前缀实际匹配到了哪一列?
光看 key 字段不够,得进 used_key_parts 看真实生效的列。
- 执行
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE shop_id = 123 AND user_id = 456 ORDER BY created_at DESC; - 在输出 JSON 里找
"used_key_parts": ["shop_id", "user_id"]—— 这说明只用到了前两列,created_at没进索引,排序靠临时文件 - 如果看到
"used_key_parts": ["shop_id"],但 SQL 明明写了user_id,大概率是user_id类型和索引定义不一致(比如一个是INT,一个是BIGINT),触发隐式转换导致索引截断
最左前缀不是黑盒规则,它是 B+ 树搜索路径的自然结果;一旦列顺序或数据类型没对齐,树就拐错弯了。










