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条件,整个索引就废了一半。
