联合索引字段顺序不能随意调换,因为B+树按定义顺序逐列排序和查找,跳过最左列或中间列、使用范围查询均会导致后续列失效;必须遵循最左前缀原则,等值条件优先、高频必查字段置左,范围条件置右,且ORDER BY顺序须严格匹配索引列序。

联合索引字段顺序错了,索引可能只用上第一列,甚至完全失效——这不是配置问题,是B+树物理结构决定的硬约束。
为什么(a, b, c)不能等价于(b, a, c)
MySQL 的联合索引底层是一棵 B+ 树,它只按定义顺序逐列排序:先按 a 全局排序,a 相同时再按 b 排,a 和 b 都相同时才按 c 排。跳过最左列(比如只查 b = ?),就像翻电话簿时不看姓氏直接找名字——根本找不到入口。
常见错误现象:EXPLAIN 显示 type: ALL 或 key: NULL,明明建了索引却没走;或者 key_len 远小于预期(比如三列索引只用了 4 字节,说明只命中了第一列)。
-
WHERE b = 2 AND c = 3→ 完全不走(a, b, c)索引 -
WHERE a = 1 AND c = 3→ 只用上a,c因中间缺b条件而失效 -
WHERE a > 10 AND b = 5→b不走索引(范围查询截断后续列)
字段顺序怎么排:按查询模式分层填
不是“哪个字段出现多就放左边”,而是按实际 WHERE / ORDER BY 中的使用方式分层排列:
-
等值条件(
=、IN)字段放最左,尤其是高频、几乎必带的过滤字段(如status、tenant_id) - 多个等值字段时,把区分度高(
CARDINALITY大)且组合后过滤效果好的放前面——例如user_id比city更唯一,但若查询总是WHERE city = 'shanghai' AND user_id IN (...),且city能先筛掉 90% 行,则city放第一更优 -
范围条件(
>、<、BETWEEN、LIKE 'abc%')必须放等值字段之后,且只能有一个,它后面的字段全部失效 -
ORDER BY / GROUP BY 字段必须与索引顺序严格一致,否则无法利用索引排序,触发
Using filesort
反例:INDEX (created_at, user_id) 对 WHERE user_id = 123 AND created_at > '2024-01-01' 只能用上 user_id(因为 created_at 是范围且在前);正确应为 (user_id, created_at)。
ALTER TABLE 修改联合索引顺序要小心什么
MySQL 不支持原地修改已有联合索引的字段顺序,本质是删旧建新,风险集中在线上大表:
- 5.7+ 的
ALGORITHM=INPLACE仅适用于增删索引,不适用于调整顺序,仍会锁表或阻塞写入 - 表数据量 > 10GB 时,直接
DROP INDEX+ADD INDEX可能导致主从延迟飙升、长事务堆积 - 务必用
pt-online-schema-change或gh-ost替代,它们能避免锁表 - 建完新索引后,立刻
DROP INDEX旧索引——冗余索引会显著拖慢 INSERT/UPDATE 性能 - 用
SELECT * FROM information_schema.STATISTICS WHERE table_name = 'xxx' ORDER BY SEQ_IN_INDEX确认新索引字段顺序是否符合预期
哪些情况调顺序收益不大
别为优化而优化。以下场景优先级很低:
- 表行数 < 1000,全表扫描比走索引还快
- 查询已走覆盖索引(
SELECT字段全在索引里),回表成本不存在 - 表写入极其频繁(如每秒上千次 UPDATE),索引维护开销可能反超查询收益
- 查询条件本身含
OR、前导LIKE '%xx'、隐式类型转换(如WHERE id = '123'但id是INT),这些会让整个索引失效,调顺序没意义
真正关键的是:先确认查询是否真能走索引(看 EXPLAIN 的 key_len 和 Extra),再谈顺序——否则所有调整都是空中楼阁。


















