范围查询使联合索引后续字段失效,因b+树字典序仅在等值路径有效;a>10后b、c无法有序跳转,只能回表过滤;确认c未走索引需看key_len和using where。

范围查询会让联合索引后续字段彻底失效,这不是配置问题或版本 bug,而是 B+ 树的遍历逻辑本身决定的。
为什么 a > 10 一出现,b 和 c 就不走索引了
联合索引 idx(a, 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' 做索引内跳转,只能回表后逐行过滤。
-
IN不触发右侧失效,因为它本质是多个=,每条路径仍保持b、c有序 -
>=、、<code>BETWEEN虽带“等号”,但只要不是单点(如a >= 10),就和a > 9等价,同样截断 -
!=、NOT IN、IS NOT NULL通常等效于全范围扫描,也会让右侧失效
怎么确认 c 真的没走索引
别只看 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 status = 'active' AND city = 'shanghai' AND created_at > '2024-01-01',status几乎每条都带 → 放第一列;city和status组合过滤效果好 → 放第二列;created_at是范围 → 必须放最后 - 如果既有
WHERE a = ? AND b = ?,又有WHERE b = ? AND c = ?,别硬塞进一个索引,考虑建两个:(a,b)和(b,c)
最易被忽略的是:索引顺序不是靠直觉或字段重要性排的,而是由你真实 SQL 中的 WHERE 条件组合、等值/范围占比、以及 ORDER BY 字段共同决定的——写错一条条件,整条索引链就断了。











