联合索引中范围查询(如a > 10)会使该列之后所有列(b、c等)失效,因后续列在b+树中不再全局有序,无法定位;explain中key_len和type可验证失效程度。

联合索引第一列用 >、、<code>BETWEEN 等范围条件时,后续列索引确实会失效——这不是 MySQL 的 bug 或配置问题,而是 B+ 树的搜索逻辑决定的。
联合索引的 B+ 树结构决定了“范围即断点”
联合索引 idx(a, b, c) 的底层是一棵按字典序排序的 B+ 树:先按 a 排,a 相同再按 b 排,a 和 b 都相同再按 c 排。这种有序性是“嵌套依赖”的。
一旦 a 出现非等值范围(比如 a > 10),匹配到的节点就不再是单条路径,而是一段子树区间。此时,这些子树中 b 的值不再全局有序——不同 a 分支下的 b=5 可能散落在树的不同层级,无法构成连续索引片。优化器无法用 b 或 c 做快速跳转,只能回表或全量扫描该范围内的行。
这和 IN 不同:a IN (1,3,5) 本质是多个等值查找,每条路径上 b、c 依然有序;而 a > 10 是不可枚举的连续区间,破坏了后续列的可定位性。
EXPLAIN 中 key_len 和 type 能直接验证失效程度
key_len 显示实际用到的索引字节数,type 表示访问类型。两者结合就能判断哪几列生效:
- 查
WHERE a = 1 AND b > 2 AND c = 3:若key_len只覆盖a和b字段长度,type为range,说明c没走索引 - 查
WHERE a > 1 AND b = 2:即使key_len看似包含a和b,只要Extra出现Using where(而非Using index),就代表b = 2是在引擎层回表后过滤的,不是索引下推 - 强制加
FORCE INDEX并不能让c生效——它只影响是否用这棵树,不改变树内搜索逻辑
哪些写法看似“绕开”,实则无效
有人尝试用 IN 替代范围、或拆成 UNION ALL,但效果有限:
-
a IN (11,12,13,...,99):如果值太多,SQL 长度和执行计划开销剧增;若值稀疏,仍无法保证b有序 -
SELECT ... WHERE a = 11 AND b = 2 UNION ALL SELECT ... WHERE a = 12 AND b = 2 ...:语义等价,但维护成本高、优化器可能拒绝合并执行计划 -
ORDER BY a, b LIMIT 10加范围谓词:即便有ORDER BY,只要WHERE中a是范围,b就无法用于索引排序,仍会触发Using filesort
真正有效的解法只有两个方向:要么把关键过滤字段前移到联合索引左侧(如业务允许,建 idx(b, a, c)),要么用覆盖索引减少回表(如只查 a 和 b,避免触碰 c)。
容易被忽略的隐含代价:回表放大效应
即使 WHERE a > 10 只返回 100 行,若后续 b = 20 实际只命中其中 1 行,MySQL 仍要对这 100 行全部回表取数据再过滤。当表行数大、回表 I/O 成本高时,这个“看似小范围”的查询可能比全表扫描还慢。别只盯着 rows,要看 Handler_read_rnd 是否飙升。











