WHERE 条件字段必须是索引的最左前缀
MySQL 的 B+ 树索引生效依赖「最左前缀匹配」,不是 WHERE 里出现的字段就自动能走索引。比如有复合索引
INDEX idx_user_status_created (user_id, status, created_at),以下查询能用上索引:
WHERE user_id = 123
WHERE user_id = 123 AND status = 'active'
WHERE user_id = 123 AND status = 'active' AND created_at > '2024-01-01'
但这些不行:
WHERE status = 'active'(跳过
user_id,无法命中)
WHERE created_at > '2024-01-01'(完全不满足最左前缀)
WHERE status = 'active' AND created_at > '2024-01-01'(缺少
user_id)
执行前务必用
EXPLAIN看
key和
possible_keys是否匹配预期,别凭感觉。
避免在索引列上做计算或函数操作
对索引字段使用函数、表达式或类型转换,会直接导致索引失效。常见踩坑点:
WHERE YEAR(created_at) = 2024→ 改成
WHERE created_at >= '2024-01-01' AND created_at
WHERE UPPER(name) = 'JOHN'→ 改成建函数索引(MySQL 8.0+):
CREATE INDEX idx_name_upper ON users ((UPPER(name))),或统一存大写
WHERE age + 1 > 30→ 改成
WHERE age > 29
注意:MySQL 8.0 支持函数索引,但低版本只能靠冗余字段或改写条件。
ORDER BY 和 GROUP BY 要尽量复用索引顺序
如果查询含
ORDER BY a, b,而索引是
(a, b, c),就能避免 filesort;但如果索引是
(b, a)或
(a, c, b),就无法覆盖排序需求。
同样,
GROUP BY也遵循最左前缀,且要求顺序一致。例如:
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id, status;
对应索引应为
INDEX idx_user_status (user_id, status),而非
(status, user_id)—— 后者可能触发临时表 + filesort。
如果
ORDER BY是
DESC,MySQL 8.0+ 支持在索引中显式声明方向:
INDEX idx_created_desc (created_at DESC);5.7 及以前,混合 ASC/DESC 会退化为 filesort。
区分等值查询和范围查询的位置
在复合索引中,**等值条件(=、IN)应放在范围条件(>、
例如索引
(a, b, c):
WHERE a = 1 AND b > 10 AND c = 5→
c不会走索引(
b是范围,中断了最左前缀)
WHERE a = 1 AND c = 5 AND b > 10→ 同样无效,顺序不影响解析逻辑,MySQL 按索引定义顺序匹配 正确设计应把高频等值字段前置,范围字段放最后,如
(a, c, b)适配
a = ? AND c = ? AND b > ?
实际建索引前,先用
SELECT DISTINCT或慢日志分析字段的过滤选择性,高选择性字段(如
user_id)比低选择性字段(如
status,只有 active/inactive)更适合放前面。
索引不是越多越好,每多一个索引都增加写开销和内存占用。重点盯住慢查询日志里的
Rows_examined和执行计划中的
type(至少到
range,最好
ref或
const)。真正难的是平衡多条语句的索引诉求——一条语句想要
(a,b),另一条想要
(b,c),这时候得权衡冗余索引还是拆分查询。
