联合索引(a,b,c)仅构建一棵b+树,排序严格按a→b→c顺序:a全局有序,b仅在a相等时有序,c仅在a、b均相等时局部有序;跳过最左字段或范围查询导致后续字段无序,索引失效是b+树物理结构决定的必然结果。

不是“必须遵循规则”,而是B+树物理结构决定了它只能这么查。
联合索引 (a, b, c) 实际只建了一棵树,不是三棵树
很多人误以为联合索引是分别对 a、(a,b)、(a,b,c) 各建一棵树。事实是:MySQL 只构建一棵 B+ 树,排序逻辑严格为「先按 a 升序;a 相等时,再按 b 升序;a 和 b 都相等时,才按 c 升序」。
-
a列全局有序 → 支持二分查找,比如WHERE a = 5可快速定位起点 -
b仅在每个a值内部有序 → 没有确定的a,b就是散列的,WHERE b = 2无法跳转到任何子树 -
c更只在a和b都固定时才形成一段局部有序区间 → 单独查c或跳过b查c,引擎根本不知道从哪开始扫
WHERE b = 2 为什么一定走全表扫描
执行计划里 key 为 NULL、type 是 ALL 或 index,就说明没用上索引。根本原因是:B+ 树非叶节点只存 a 的分割值(如 a ≤ 10 → 左子树),没有 a 条件,优化器连该往左还是右走都不知道。
- 即使写成
WHERE b = 2 AND a = 1,优化器会重排为a = 1优先,但WHERE b = 2单独出现时,毫无机会 -
EXPLAIN中possible_keys为空,不是优化器“没选对”,而是压根没候选索引可选 - 哪怕表只有 10 行,只要没
a条件,就触发全表扫描 —— 这不是 bug,是 B+ 树的必然行为
范围查询让右边字段失效,是因为“有序性断裂”
假设索引是 (gid, cid, sid),WHERE gid = 1 AND cid > 10 AND sid = 100 中,sid 不会走索引。关键不在“范围之后不能用”,而在于:cid > 10 导致在 gid = 1 的数据块内,sid 不再保持连续有序。
-
gid = 1 AND cid = 5→sid在这个子集里是有序的,可二分 -
gid = 1 AND cid > 10→ 扫描的是多个cid值对应的所有sid,这些sid在磁盘上是跳跃分布的,B+ 树无法跳转定位 - 所以
sid = 100最终靠Using where逐行判断,不是索引驱动
真正容易被忽略的点是:最左匹配不是语法检查,而是数据物理组织方式的硬约束。设计索引时,别只看 WHERE 条件写了几个字段,得想清楚——那个“最左字段”是否真能筛掉大部分数据?如果 a 区分度极低(比如只有 2 个值),那哪怕严格满足最左原则,效果也接近全表扫描。











