联合索引在MySQL 5.7中跳过首列即失效,因其B+树按(a,b,c)字典序严格排序,仅a驱动分支定位;缺失a则无法确定起始区间,且5.7不支持Index Skip Scan,优化器只能全表扫描。
MySQL 5.7 的 InnoDB 引擎对联合索引的实现完全依赖 B+树的有序性规则:索引项按 (a, b, c) 的字典序严格排序。这意味着整棵树的分支逻辑只由第一列 a 驱动;只有当查询给出 a 的具体值(或范围),才能快速定位到某一段叶子节点区间。一旦跳过 a,比如只查 WHERE b = 'x',引擎无法从根节点开始“跳”到某个 b 值对应的子树——因为不同 a 值下的 b 是交错存储的,b 本身在整个索引中并不全局有序。
这和单列索引完全不同:INDEX(b) 的 B+树是按 b 单独排序的,所以能直接二分查找;而 INDEX(a,b) 中的 b 只在每个 a 分组内有序。
MySQL 8.0.13 引入的 skip_scan 是一个主动探测机制:它会枚举首列 a 的所有去重值(如 a IN ('A','B','C')),对每个值构造等值条件,再分别走索引查找 b。但 MySQL 5.7 的优化器压根不具备这个能力,它的索引选择逻辑是原子性的——要么整条索引可用,要么全表扫描。
常见错误现象包括:
EXPLAIN 显示 type: ALL,key: NULL
SHOW INDEX FROM tbl 确认索引存在且顺序无误,但就是不用你不能靠改写 SQL(比如加 ORDER BY a 或 FORCE INDEX)让 5.7 “强行启用”跳过首列的联合索引——底层没这个路径。
在 MySQL 5.7 环境下,没有捷径可走。必须显式适配其索引匹配规则:
WHERE b = ?,就建单独索引:ALTER TABLE t ADD INDEX idx_b (b)
c,但总缺 a,考虑 INDEX(b, c) 或 INDEX(b, c, a)(把高频过滤列前置)OR、UNION 或子查询“骗过”优化器来复用原联合索引——它们通常导致更差性能或额外临时表注意:即使你把联合索引改成 (b, a, c),只要业务查询仍以 a 为条件主干,又会面临新问题——a 的选择性可能低,导致索引效率下降。索引设计必须和实际 WHERE 模式对齐,不是单纯“包含字段”就行。
最易被忽略的一点:MySQL 5.7 不会告诉你“这个索引本可以跳过首列用”,它只会沉默地选全表扫描。你得靠 EXPLAIN 和慢日志主动发现这类失效,而不是等线上报警。