a > 10使idx(a,b,c)中b、c失效,因b+树按a→b→c嵌套排序,a范围查询导致后续列物理无序,无法索引下推;explain中key_len小、extra含using where、rows大即表明失效。

为什么a > 10会让idx(a, b, c)里的b和c失效
因为B+树的排序是嵌套依赖的:先按a排,a相同再按b排,a和b都相同才按c排。一旦a > 10,MySQL就无法落在单条路径上,而必须扫描所有a > 10对应的所有子树分支。这些分支里,b值不再全局有序(比如a=11下的b=5和a=12下的b=5在物理页上可能相距很远),c更是彻底无序。优化器没法用b = 20或c = 'x'做索引内跳转,只能回表后逐行过滤。
EXPLAIN里怎么看右侧列是否真的失效
关键看两个字段:key_len和Extra:
-
key_len只覆盖前N字节,说明只有前N列参与了索引查找;例如idx(a,b,c)中a是INT(4字节)、b是VARCHAR(20)(76字节),若key_len = 4,代表只有a用了索引 -
Extra出现Using where而非Using index,说明b或c的条件是在回表后做的过滤,不是索引下推 -
type是range但rows异常大,也暗示后续列没起到过滤作用
IN不截断右侧,但>=、BETWEEN、LIKE 'abc%'会
区别在于是否保留“等值路径”能力:
-
a IN (1,3,5)本质是三次=查找,每次路径上b和c依然有序,右侧列可用 -
a >= 10等价于a > 9,是连续不可枚举区间,破坏单点定位,右侧列立即失效 -
LIKE 'abc%'算前缀等值匹配,不触发截断;但LIKE '%abc'该列直接不走索引,连最左前缀都不满足 -
!=、NOT IN、IS NOT NULL通常被优化器视作全范围扫描,同样导致右侧失效
怎么改才能让b = 20也走索引
不能靠调整WHERE里条件的书写顺序(WHERE b = 20 AND a > 10和WHERE a > 10 AND b = 20执行计划完全一样),真正有效的是重构索引本身:
- 把高选择性、常用于等值查询的列前置:比如
b是状态码(取值离散),a是时间戳,建idx(b, a, c) - 如果
a和b都高频范围查询,不要强求一个联合索引覆盖,宁可建两个窄索引:idx(b)和idx(a),让优化器用index_merge - 避免把极易范围查询的字段(如
created_at)放在联合索引最左,否则一用就废掉右边所有列
最易被忽略的一点:索引失效不是“没建好”,而是B+树结构对范围查询的天然限制——它不是配置问题,也不是MySQL bug,你没法绕过这个数学边界,只能按搜索逻辑反推索引设计。











