复合索引范围查询“切断”右侧字段,因B+树仅在前导列值完全相等时保证后续列有序;一旦出现范围条件(如a>10),b、c便失去全局有序性,无法用于索引查找。

范围查询为什么直接“切断”复合索引右侧字段
因为复合索引底层是 B+ 树,数据按 (a, b, c) 字典序严格排序:先排 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' 做索引内跳转,只能回表后逐行过滤。
这不是 MySQL 的 bug,是 B+ 树索引能力的数学边界——范围破坏了“单一分组内有序”这个前提。
怎么确认后续列真没走索引
别只看 EXPLAIN 的 key 字段是否显示索引名,关键盯两个指标:
-
key_len:表示实际用到的索引字节数。对照索引定义算出各字段理论长度(如INT是 4 字节,VARCHAR(50)在 utf8mb4 下最多 200 字节),就能反推出用到了第几个字段 -
Extra中是否出现Using where:如果写了WHERE a = 1 AND b > 2 AND c = 3,但Extra是Using where而非Using index,说明c = 3是回表后过滤的,没参与索引查找
例如索引为 (a INT, b VARCHAR(50), c DATETIME),查 a = 1 AND b > 10 AND c = '2023-01-01' 时,若 key_len = 24(刚好够 a + b),就证明 c 没参与索引查找。
哪些“绕开”写法其实无效
很多人试图用语法变形规避,但多数只是徒增复杂度,不解决根本问题:
- 把
a > 10拆成a IN (11,12,13,...,100):值一多,SQL 长度爆炸,执行计划开销剧增;且若数据稀疏,b 依然无法保证连续 - 用
UNION ALL拆成多个等值查询:语义等价,但维护成本高,优化器可能拒绝合并,还容易触发临时表或文件排序 - 加
FORCE INDEX:它只影响“用不用这棵树”,不改变树内搜索逻辑,c 还是没法加速 -
ORDER BY a, b LIMIT 10配合WHERE a > 10:排序字段即使出现在索引里,只要 WHERE 中 a 是范围,b 就无法用于索引排序,大概率触发Using filesort
真正有效的解法只有两个方向
要么调整索引设计,要么收敛查询意图:
- 把高频等值过滤的字段左移,把大概率范围查询的字段(如时间、金额、年龄)尽量靠右,避免卡在中间“腰斩”后续列
- 如果业务中存在
WHERE a = ? AND c = ?(跳过 b)和WHERE b > ? AND c = ?两种模式,不要强求一个INDEX(a,b,c)覆盖全部,宁可建两个更窄的索引:INDEX(a,c)和INDEX(b,c) - 覆盖索引(
SELECT col1,col2 FROM t WHERE ...且索引包含所有 SELECT 和 WHERE 字段)能减少回表,但不能挽救范围导致的右侧列失效
最常被忽略的一点:索引列顺序 ≠ WHERE 条件书写顺序。写成 WHERE b = 5 AND a > 10 不会让 b 走索引——除非你把索引改成 (b, a)。


















