复合索引字段顺序不能随便调换,因为mysql b+树索引遵循最左前缀匹配原则:只有从左开始连续的等值条件字段才能生效,范围查询字段后所有字段失效;高频等值字段应置左,范围字段靠右且仅保留一个,排序字段可追加末尾。

复合索引字段顺序为什么不能随便调换
MySQL 的 B+ 树索引是按字段顺序逐层排序的,WHERE a = 1 AND b = 2 AND c > 3 能用上 INDEX(a, b, c),但换成 INDEX(b, a, c) 就只能用上第一个字段 b,后面两个失效。这是因为索引结构决定了“最左前缀匹配”——只有从左开始连续的字段满足等值条件,后续字段才能参与范围查询或排序。
实操建议:
- 把高频等值查询字段放最左(比如
user_id = ?、status = 'active') - 范围查询字段(
>、、<code>BETWEEN、LIKE 'abc%')尽量靠右,且只保留一个,它之后的字段无法被索引使用 - 排序字段(
ORDER BY)如果和查询条件字段重叠,优先保证查询过滤性;若不重叠,可追加到复合索引末尾(如INDEX(a, b, created_at)支持WHERE a=1 ORDER BY created_at)
ALTER TABLE ADD INDEX 语法与常见报错
添加复合索引直接用 ALTER TABLE,但容易踩几个坑:
典型错误:ERROR 1061 (42000): Duplicate key name 'idx_user_status_time' —— 索引名重复,不是字段重复
正确写法:
ALTER TABLE orders ADD INDEX idx_user_status_time (user_id, status, created_at);
注意点:
- 索引名必须唯一,建议带表名前缀避免冲突
- 字段名之间用英文逗号分隔,**不能加括号嵌套**(如
(user_id, status), created_at是错的) - 如果表很大,
ADD INDEX会锁表(5.6+ 支持ALGORITHM=INPLACE,但仍有条件限制),线上操作前务必在从库或低峰期验证耗时 - 已有单列索引(如
INDEX(user_id))和新复合索引(INDEX(user_id, status))共存时,前者基本冗余,可删
EXPLAIN 验证是否真用上了复合索引
加完索引别急着走,EXPLAIN 才是最终裁判。关键看三列:
-
type:至少要是ref或range,ALL表示全表扫描 -
key:显示实际使用的索引名,确认是不是你刚建的那个 -
key_len:数值越大通常说明用到的字段越多,比如key_len = 8对应INT + TINYINT组合,若远小于预期,说明后缀字段没生效
示例:
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 'paid' ORDER BY created_at DESC;
如果 key 显示 idx_user_status_time,key_len 是 9(假设 user_id 占4、status 占1、created_at 占4),说明三个字段都参与了索引查找或排序;若 key_len = 5,大概率 created_at 只用于排序,未用于过滤。
哪些情况加复合索引反而拖慢写入
索引不是越多越好,每个新增的复合索引都会带来额外开销:
- INSERT/UPDATE/DELETE 时,所有相关索引都要同步更新,字段越多、越长,写入越慢
- 字符串字段(如
VARCHAR(255))做索引要谨慎,考虑用前缀索引(INDEX(title(50))),但复合索引里不支持对中间字段截断 - 低区分度字段(如
gender只有 'M'/'F')放在复合索引左侧会严重削弱选择性,应往后挪或干脆排除 - 如果查询中
OR条件占主导(如WHERE a = 1 OR b = 2),复合索引基本无效,得考虑拆成两个单列索引 +UNION,或者改用全文索引、ES
真正影响性能的,往往不是“有没有索引”,而是字段顺序是否贴合查询模式、以及是否在写多读少的表上堆砌了过多索引。











