联合索引字段顺序错一位会导致查询性能断崖式下降——因b+树搜索路径被物理截断,最左前缀是搜索逻辑而非语法约定;等值匹配中断、范围查询、order by顺序错位均使后续字段失效;高基数字段未必优先,应依查询模式排序;调整顺序需重建索引,线上须谨慎。

联合索引字段顺序错一位,查询可能从毫秒级掉到秒级甚至超时——这不是统计偏差,而是B+树搜索路径被物理截断的结果。
最左前缀不是语法约定,是B+树的搜索逻辑
MySQL的联合索引底层是B+树,它按定义顺序逐列比较键值。一旦某列没参与等值匹配(= 或 IN),后续列就无法进入搜索路径。
-
INDEX (a, b, c):WHERE a = 1 AND b = 2 ✅ 能定位到具体叶子节点区间;WHERE a = 1 AND c = 3 ❌ b缺失,c无法比较,只能扫a=1的所有行再内存过滤 - WHERE b = 2 AND c = 3 ❌ 整个索引完全失效,type变成
ALL - 范围查询(
>、BETWEEN)同样会截断:WHERE a = 1 AND b > 10 AND c = 5 → c不走索引
ORDER BY字段顺序错位直接触发Using filesort
排序无法复用索引顺序时,MySQL必须额外做一次排序,开销随数据量非线性增长。尤其配合LIMIT偏移时,性能断崖下跌。
- 查询:
SELECT * FROM orders WHERE user_id = 100 ORDER BY created_at DESC→ 索引必须是(user_id, created_at),不能是(created_at, user_id) - MySQL 8.0+支持方向定义,但5.7及以前要求全部ASC或全部DESC;
ORDER BY a ASC, b DESC在旧版本中,b字段无法利用索引排序 - 即使WHERE和ORDER BY字段都存在,只要顺序不严格一致,
Extra里就会出现Using filesort
高基数字段放前面 ≠ 绝对正确
字段选择性(区分度)只是参考,真正决定顺序的是查询模式本身。高频、必带、等值的字段才该优先。
- status字段只有
'active'/'inactive'两个值,选择性极低,但如果90%查询都带WHERE status = 'active',它就必须放第一列——否则索引根本起不来 - city字段有几百个值,但单独查
WHERE city = 'shanghai'很少,和status组合后过滤率高达95%,适合放第二列 - created_at时间戳区分度最高,但常带范围条件(
> '2024-01-01'),必须放最后,否则后面所有列全废
调整顺序本质是删索引重建,线上务必谨慎
MySQL不支持原地修改联合索引字段顺序。所谓“调整”,实际是DROP INDEX + ADD INDEX,期间索引不可用,且可能锁表。
- 5.7+虽支持
ALGORITHM=INPLACE,但仅限增删索引,不适用于改顺序 - 表体积>10GB时,直接
ALTER TABLE极易引发主从延迟或长事务阻塞,应使用pt-online-schema-change或gh-ost - 建完新索引后,必须立刻
DROP旧索引,否则冗余索引会拖慢所有写操作 - 用
SELECT * FROM information_schema.STATISTICS WHERE TABLE_NAME = 'tbl' AND INDEX_NAME = 'idx_name'核对SEQ_IN_INDEX是否符合预期
最常被忽略的一点:索引顺序优化只对高频、固定模式的查询有效。如果WHERE条件动态拼接、字段组合多变,强行塞进一个联合索引不如建两三个精准覆盖的索引,或者考虑生成列+函数索引替代方案。











