MySQL多列索引等值条件必须从左到右连续才生效:如索引(a,b,c),WHERE a=1 AND c=3仅用a,b缺失导致c失效;ORDER BY/GROUP BY也需连续前缀且类型兼容;key_len、rows与filtered需结合判断真实过滤效果。

MySQL 多列索引中等值条件必须「从左到右连续」才生效
MySQL 优化器对 WHERE 中的等值条件(= 或 IN)是否能利用多列索引,核心判断是:是否覆盖索引最左侧连续前缀。不是“只要字段在索引里就用”,而是“左边断了,右边全废”。
比如索引是 (a, b, c):
-
WHERE a = 1 AND b = 2 AND c = 3→ 全部走索引(range 或 ref) -
WHERE a = 1 AND c = 3→ 只用上a,c不参与索引过滤(b缺失导致断层) -
WHERE b = 2 AND c = 3→ 索引完全失效(a没出现,无法定位起始位置)
注意:IS NULL 在某些版本中可视为等值条件,但 !=、<>、LIKE '%xxx' 会截断等值链。
为什么 ORDER BY 和 GROUP BY 也依赖同一前缀规则
排序和分组能否避免 Using filesort 或 Using temporary,同样要看是否满足索引最左前缀——而且要求更严格:必须是**连续、无跳过、类型兼容**的等值条件之后的字段。
仍以 (a, b, c) 索引为例:
-
WHERE a = 1 ORDER BY b, c→ 可用索引排序(等值后剩余字段连续) -
WHERE a = 1 AND b > 10 ORDER BY c→ 可用(b是范围,但c是它后面的唯一字段) -
WHERE a = 1 ORDER BY c→ 不行(b被跳过,c不在前缀链上) -
WHERE a = 1 ORDER BY b DESC, c ASC→ 5.7+ 支持混合方向,但老版本只认全 ASC 或全 DESC
隐式类型转换(如字符串字段查数字)会让整个前缀失效,哪怕看起来“字段都对上了”。
索引过滤性 ≠ 字段选择性,优化器会综合评估「实际可下推的条件数量」
很多人以为加个高选择性字段(比如 user_id)到联合索引开头就一定好,但 MySQL 优化器真正看的是:WHERE 条件中,**有多少列能构成连续前缀并实际用于快速定位数据页**。
例如表有 1000 万行,索引 (status, create_time, user_id):
-
WHERE status = 'active' AND user_id = 123→ 实际只用status,user_id因断层被忽略,可能扫几百万行 -
WHERE status = 'active' AND create_time > '2024-01-01'→ 用status定位起点,再按create_time范围扫描,效率可能更高
执行计划里的 key_len 是关键信号:它显示实际用了索引多少字节。如果 key_len 明显小于索引总长度,说明右侧字段没参与过滤。
EXPLAIN 的 rows 和 filtered 需要一起看,单看一个会误判
rows 是优化器预估的“扫描行数”,filtered 是该条件下“剩余行占扫描行的比例”。两者相乘 ≈ 最终返回行数,这才是过滤效果的真实体现。
常见误读场景:
-
rows=1000000, filtered=0.1→ 实际处理 10 万行,比rows=10000, filtered=100(10000 行)压力大得多 - 当
filtered极低(如 1),但rows很小,说明索引定位准,只是单行内计算多(比如 JSON 字段匹配) -
filtered为 100 并不意味“全匹配”,只是优化器没做更细估算(尤其对函数、表达式)
真实瓶颈常藏在 filtered 后面:你以为索引过滤了 99%,结果 filtered=10,意味着 90% 的工作还在 server 层做。


















