mysql索引列顺序如何确定_mysql索引优化经验

来源:这里教程网 时间:2026-02-28 20:37:43 作者:

WHERE 条件中字段的出现顺序决定索引列顺序

MySQL 的 B+ 树索引是按列顺序逐级排序的,

WHERE
中多个条件是否能走索引、走多少,直接取决于它们是否构成「最左前缀」。比如你常写
WHERE status = ? AND user_id = ? AND created_at > ?
,那索引就该建为
(status, user_id, created_at)
,而不是反过来。

常见错误是把高区分度字段(如

user_id
)放最左,以为“效率更高”——但若查询从不单独或优先用它,这个设计反而让其他组合失效。

先列出所有高频
WHERE
组合,挑出共有的最左字段作为首列
等值查询字段(
=
IN
)放前面,范围查询(
>
BETWEEN
LIKE 'abc%'
)靠后,且只能有一个范围列在等值列之后
如果存在
ORDER BY
字段,且无额外
filesort
,需确保其顺序被索引覆盖(例如
ORDER BY user_id, created_at
可被
(user_id, created_at)
索引满足)

联合索引中哪些列不该放进索引

不是所有

WHERE
出现过的字段都该塞进一个索引。重复、低效、干扰最左匹配的列会拖慢写入、增大 B+ 树层级、降低缓存命中率。

典型冗余场景:

WHERE a = ? AND b = ? AND c > ? ORDER BY d
,有人建
(a, b, c, d)
——但若
d
仅用于排序且数据量不大,不如单独建
(a, b, c)
,让 MySQL 用主键回表取
d
,反而比大索引更省空间、更快更新。

SELECT
列一般不进索引(除非是覆盖索引场景,且明确需要避免回表)
频繁
UPDATE
的列慎入索引,尤其是非必要排序/过滤字段
布尔字段(如
is_deleted TINYINT(1)
)区分度极低,单独做索引列几乎无效;只有和其他高区分度字段组合时才可能有用
TEXT
/
VARCHAR(2000)
类长字段,如必须参与索引,记得指定前缀长度(
INDEX idx_x (content(255))
),否则建索引失败或膨胀严重

EXPLAIN 结果里 key_len 值怎么帮你看清索引使用程度

key_len
是判断 MySQL 实际用了索引哪几列的关键线索。它不是“索引长度”,而是“本次查询用到的索引字节数”。结合字段定义反推,就能知道有没有截断、有没有跳过中间列。

比如

user_id BIGINT UNSIGNED
占 8 字节,
status TINYINT
占 1 字节,
created_at DATETIME
占 5 字节(无秒精度)或 8 字节(有)。若索引是
(status, user_id, created_at)
,而
EXPLAIN
显示
key_len = 9
,说明只用了前两列(1 + 8),第三列没生效——大概率是
WHERE
里没出现
created_at
,或用了
!=
/
IS NULL
等无法走索引的操作。

key_len
前先确认字段是否允许
NULL
:允许则每字段多占 1 字节(NULL 标志位)
字符集影响大:
utf8mb4
VARCHAR(100)
最大占 400 字节,但实际
key_len
只算当前值长度 + 长度头(1 或 2 字节)
如果
key_len
比预期小,别急着加列,先检查
WHERE
条件是否符合最左前缀,以及是否有隐式类型转换(如字符串字段传数字)

ORDER BY 和 GROUP BY 共用索引时的陷阱

很多人以为只要索引包含

ORDER BY
字段就能避免
Using filesort
,但忽略了方向一致性。MySQL 要求索引顺序与
ORDER BY
方向完全一致(全 ASC 或全 DESC),混合方向(如
ORDER BY a ASC, b DESC
)在 8.0 以前基本无法利用索引排序。

另外,

GROUP BY
在无聚合函数时本质是去重 + 排序,同样依赖索引顺序。若写
GROUP BY region, city
却建了
(city, region)
索引,不仅无法加速分组,还可能触发临时表 + filesort。

MySQL 8.0+ 支持降序索引(
INDEX idx_x (a DESC, b ASC)
),但需显式声明,且不能和升序混用在同一索引中
如果
ORDER BY
WHERE
字段有重叠,优先保证
WHERE
最左前缀完整,再把排序字段追加在后(如
WHERE a = ? AND b > ? ORDER BY c
→ 索引
(a, b, c)
GROUP BY
后带
WITH ROLLUP
会强制使用临时表,此时索引优化意义不大,应考虑物化汇总表
EXPLAIN SELECT * FROM orders 
WHERE status = 1 AND user_id = 12345 
ORDER BY created_at DESC;

真正难的不是记规则,是每次加索引前,得翻一遍慢查日志里真实的

WHERE
ORDER BY
组合,再对着
EXPLAIN
里的
key_len
Extra
一列一列对齐——漏掉一个隐式转换或一个
OR
条件,整个索引就废了一半。

相关推荐