联合索引必须遵循最左前缀原则,跳过最左列会导致整个索引失效;正确顺序应为高频等值列优先、高区分度列靠左、范围列置后,并通过explain验证执行计划。

联合索引顺序错位直接导致索引失效
mysql 的 B+ 树索引是严格按定义顺序逐列比较的,一旦 WHERE 条件跳过最左列(即“断层”),后续所有列都无法参与索引查找。比如建了 INDEX idx_user (city, age, status),但查询写成 WHERE age = 25 AND status = 'active',EXPLAIN 中 key 字段会显示 NULL,type 是 ALL,rows 接近全表行数——本质就是退化为全表扫描。
- 不是“部分生效”,而是整个联合索引基本不被使用
- 即使只漏掉第一个字段,优化器也无法定位起始叶子节点,B+ 树失去有序遍历基础
- ORDER BY 或 GROUP BY 的列顺序/方向若与索引不一致,也会触发
using filesort,哪怕 WHERE 已命中索引
哪些查询能真正用上联合索引
只有满足“最左前缀 + 连续等值 + 范围靠后”三要素,索引才能高效工作。以 INDEX idx_log (app_id, event_type, created_at) 为例:
-
WHERE app_id = 100→ 用第 1 列 -
WHERE app_id = 100 AND event_type IN ('click', 'submit')→ 用前 2 列(IN 视为等值集合) -
WHERE app_id = 100 AND event_type = 'click' AND created_at > '2024-01-01'→ 全部 3 列参与,但注意:created_at是范围条件,它右侧不能再有用于查找的列(不过仍可做 ICP 过滤) -
WHERE app_id > 100 AND event_type = 'click'→event_type只能做索引条件下推(ICP),不能用于快速定位
索引列顺序怎么排才合理
顺序不是按字段名字母排,也不是按建表顺序堆砌,核心逻辑是:高频等值过滤列优先,高区分度列靠左,范围列放最后。
- 错误示范:
INDEX (status, region, created_at)——status只有 ‘active’/’inactive’,基数太低,作为首列筛选后仍剩大量数据,索引效率差 - 正确思路:先看 WHERE 中哪些条件几乎每次都出现(如
user_id、tenant_id),再看哪个字段值越分散越好(如created_at比status更适合前置) - 如果某列常用于 ORDER BY,且方向固定(如
score DESC),它应紧接在等值列之后,并与索引定义方向一致,否则排序无法复用索引
如何验证和修复索引顺序问题
别猜,用 EXPLAIN 看真实执行路径;别忍,该重建就重建——联合索引顺序改了必须 DROP 再 CREATE,ALTER 不支持重排。
- 检查命令:
EXPLAIN select * FROM orders WHERE user_id = 123 AND status = 'paid';,重点盯key是否非空、rows是否明显小于总行数 - 发现失效后,先确认查询模式是否稳定:如果
WHERE status = ?是高频独立查询,那原联合索引设计本身就有缺陷,得补单列索引或重构联合索引 - 重建索引前,用
ANALYZE table orders;更新统计信息,避免优化器因旧数据误判
索引顺序一旦定错,不是性能打七折八折,而是从“毫秒级响应”滑向“秒级甚至超时”。最容易被忽略的是:开发阶段数据量小,问题不暴露;上线后数据增长十倍,原来能跑的 SQL 突然变慢——这时候再查,往往第一反应是加机器,而不是翻出那条早该重写的 CREATE INDEX 语句。