如何用 EXPLAIN
快速定位慢查询的索引问题
直接看
EXPLAIN输出比盲猜有效得多。重点盯住
type、
key、
rows和
Extra这四列:
type值为
ALL或
index:说明走了全表扫描或全索引扫描,大概率缺合适索引
key为
NULL:没命中任何索引,哪怕表上有索引也可能是条件写法导致失效(比如对字段做函数操作)
rows明显大于实际返回行数:索引选择性差,或用了前缀索引但区分度低
Extra出现
Using filesort或
Using temporary:ORDER BY / GROUP BY 没走索引,可能需要覆盖索引或调整排序字段顺序
复合索引设计时,字段顺序为什么不能随便调换
MySQL 的 B+ 树索引是按字段顺序逐级分裂的,等价于一个嵌套字典:
{a: {b: {c: [...]}}}。这意味着:
只查 b或
c字段,该索引完全无效 查
a = ? AND b > ?可以用到
a和
b,但
c后面的范围条件会截断索引下推 查
a IN (?, ?) AND b = ?时,
b无法继续用于索引查找(IN 是范围操作,会中断后续字段的等值匹配) 高频过滤字段放最左,排序/分组字段尽量靠右并保持顺序一致(如
WHERE a = ? ORDER BY b, c→ 索引建为
(a, b, c))
SELECT *
和覆盖索引之间的性能鸿沟
即使有索引,
SELECT *很可能让优化器放弃使用它——因为回表成本太高。覆盖索引指查询所需所有字段都在索引中,无需回主键索引查数据行。 判断是否覆盖:
EXPLAIN中
Extra出现
Using index(注意不是
Using index condition) 常见误操作:给
(a, b)建了索引,但查询写成
SELECT a, b, c FROM t WHERE a = ?→
c不在索引里,必然回表 解决方案:把经常一起查的字段加进索引末尾(如改成
(a, b, c)),或明确只查索引包含的字段 注意:大字段(
TEXT、
BLOB)不能建索引,也不适合放进复合索引,否则索引体积暴涨
哪些看似合理却让索引彻底失效的写法
这些写法会让 MySQL 直接跳过索引,哪怕字段上有完美索引也没用:
在索引字段上使用函数:WHERE YEAR(create_time) = 2023→ 改成
WHERE create_time >= '2023-01-01' AND create_time隐式类型转换:
WHERE mobile = 13812345678(mobile 是
VARCHAR)→ 数字会被转成字符串再比较,触发全表扫描;应统一写成字符串
'13812345678'LIKE 以通配符开头:
WHERE name LIKE '%abc'→ 无法使用 B+ 树的有序特性;若必须模糊查前缀,确保写成
LIKE 'abc%'OR 连接不同字段:
WHERE a = 1 OR b = 2→ 即使
a和
b都有索引,也可能全表扫;考虑拆成 UNION 或补上联合索引
(a,b)并重写逻辑
EXPLAIN SELECT id, name FROM user WHERE status = 1 AND created_at > '2023-01-01' ORDER BY score DESC;
如果这个查询慢,先看
EXPLAIN是否走了
status索引;如果没有,优先建联合索引
(status, created_at, score)—— 注意顺序不是随意排的,而是按「过滤→范围→排序」的层级来组织。漏掉任何一个环节,都可能让优化器重新评估成本并弃用索引。
