联合索引未遵循最左前缀原则即失效:如索引(a,b,c),where b=1或c=2等跳过最左列a的查询无法使用索引;范围查询(>、

联合索引没走最左列,直接失效
联合索引 (a, b, c) 不是从 a 开始查,就基本等于没建。比如写 WHERE b = 1 AND c = 2,优化器连索引树根都进不去,只能全表扫描。
常见错误场景:
-
WHERE b = 1(跳过a) -
WHERE a = 1 AND c = 2(跳过中间的b,c无法命中) -
WHERE c = 2(只用最右列,完全不触发)
解决办法只有两个:要么补上最左列条件,比如改成 WHERE a = 1 AND b = 1 AND c = 2;要么按高频查询重排索引顺序,比如把常单独查的字段提到最左,如 (b, c, a)。
范围查询后列无法走索引
联合索引中,一旦某个列用了范围查询(>、、<code>BETWEEN、LIKE 'xxx%'),它右边所有列就“断联”了。
例如索引 (status, create_time, user_id):
-
WHERE status = 1 AND create_time > '2023-01-01'→user_id不会走索引 -
WHERE status = 1 AND create_time = '2023-01-01' AND user_id = 100→ 全部能用
注意:MySQL 5.6+ 的索引下推(ICP)能缓解部分问题,但只对 WHERE 中的非索引列过滤有效,对索引列本身的“断链”无济于事。
隐式类型转换或函数导致整条索引失效
哪怕只在联合索引最左列上套个函数或做隐式转换,整个索引就废了。
典型例子:
-
WHERE UPPER(a) = 'ABC'(a是索引首列) -
WHERE a = 123,但a是VARCHAR类型(触发隐式转换) -
WHERE DATE(create_time) = '2023-01-01'(create_time在联合索引里靠前)
本质是:B+Tree 存的是原始值,不是函数结果。只要索引列被“动过”,优化器就无法复用排序结构。
修复方式统一:让索引列以原始形式出现在 WHERE 左侧,右侧做等价改写。比如把 DATE(create_time) 换成 create_time BETWEEN ... AND ...。
OR 条件混入非索引列,强制放弃联合索引
哪怕联合索引本身覆盖全部查询字段,只要 OR 里夹了一个没索引的列,优化器大概率直接放弃该索引,走全表扫描。
比如索引 (name, age),但写成:
WHERE name = '张三' OR city = '北京'
即使 city 没索引,整个条件也会退化为全表扫描。
可行解法不多:
- 给
city单独加索引,触发index_merge(但性能不稳定,慎用) - 拆成
UNION ALL:分别走name索引和全表扫city,再合并结果 - 重构业务逻辑,避免跨列
OR
真正容易被忽略的是:联合索引的“生效”不是看它存不存在,而是看优化器是否愿意选它——而一个看似微小的类型不匹配或函数调用,就足以让它被排除在候选列表之外。











