MySQL联合索引设计需遵循最左前缀原则,等值查询列优先、范围查询列靠后且仅一个,ORDER BY字段需方向一致并置于等值列后;冗余、低区分度、长字段及频繁更新列应避免入索引;key_len用于验证实际使用索引列数。

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










