MySQL EXPLAIN详解:一眼看穿慢SQL

MySQL EXPLAIN详解:一眼看穿慢SQL

你找到了慢查询日志,发现一条SQL执行了5秒。你给它加了索引,还是慢。你又加了一个,还是慢。

你在猜。你不知道MySQL到底怎么执行这条SQL,是全表扫描还是用了索引?用了哪个索引?扫了多少行?在哪里花了时间?

EXPLAIN就是让你看这些的。今天把EXPLAIN的输出拆开讲,以后慢SQL在你眼里就是透明的。


先看一个数据

某运维团队统计,超过80%的SQL性能问题,通过EXPLAIN分析就能定位。不需要高级调优,不需要改架构。但前提是你会看EXPLAIN。

很多人只看了key列(用了哪个索引),没看typeExtra。索引用上了,但用得不高效,一样慢。


基础用法

sql

EXPLAIN SELECT * FROM orders WHERE user_id = 12345;

输出一张表,每一行是一个查询步骤(多表JOIN会有多行)。关键列:

列名含义重点关注
id查询执行顺序JOIN时id相同,从上到下
select_type查询类型SIMPLE简单查询,SUBQUERY子查询
table表名
type访问类型ALL最差,ref/range好,const/eq_ref最好
possible_keys可能用的索引有索引但没用上,说明写的有问题
key实际用的索引NULL表示没走索引
key_len索引长度可判断复合索引用了哪几列
rows预估扫描行数越小越好
Extra额外信息Using filesort、Using temporary是危险信号

重点看type、key、rows、Extra。


type列:访问类型(性能关键)

type从好到差排序:

type含义性能示例
system系统表,只有一行最好很少见
const主键或唯一索引等值查询极好WHERE id = 1
eq_refJOIN时被驱动表用主键关联很好JOIN ON t1.id = t2.id
ref非唯一索引等值查询WHERE user_id = 123(user_id有普通索引)
range索引范围扫描中等WHERE id BETWEEN 1 AND 100
index全索引扫描较差比ALL快一点,但仍是全扫
ALL全表扫描最差必须优化

目标:让type至少达到range,最好达到ref或更高。看到ALL就是报警。

反常识点:有时index(全索引扫描)比range还慢。索引扫描顺序和存储顺序不一致时,大量离散I/O拖慢速度。


Extra列:额外信息(危险信号)

Extra里的几个关键词要特别留意:

Extra信息含义解决方案
Using filesort需要额外排序,没用索引给ORDER BY的字段加索引
Using temporary用了临时表(通常是GROUP BY或DISTINCT)优化查询,或加索引
Using where用WHERE过滤正常,但配合其他信息看
Using index覆盖索引,不回表,说明索引已包含所有查询字段
Using index condition索引下推好,MySQL5.6+优化
Using where + Using index索引列被WHERE使用,但没覆盖全部可接受

重点:看到Using filesortUsing temporary,通常需要优化。这两项在数据量大时极其影响性能。


实战案例一:慢在filesort

sql

EXPLAIN SELECT * FROM users ORDER BY create_time LIMIT 10;

输出:

  • type: ALL(全表扫描)
  • Extra: Using filesort

问题:没有索引,MySQL要全表扫,再排序。给create_time加索引:

sql

ALTER TABLE users ADD INDEX idx_create_time (create_time);

再看EXPLAIN:

  • type: index(用了索引)
  • Extra: 无filesort

排序走索引,不再额外排序。百万级数据查询从2秒降到0.01秒。


实战案例二:复合索引顺序错误

sql

EXPLAIN SELECT * FROM orders WHERE status = 'paid' AND create_time > '2026-01-01';

有索引(create_time, status),但type是rangekey_len只用了4字节(只用了create_time)。status没用到索引。

把索引顺序改成(status, create_time),因为status =等值查询放前面,create_time >范围查询放后面。

改后:

  • type: range
  • key_len用了更多字节,两个条件都用到索引

规则:复合索引中,等值查询放前面,范围查询放后面。


key_len:判断复合索引用了多少列

key_len表示索引使用的字节数。可以反推用了复合索引中的哪几列。

假设复合索引(status, create_time)

  • status是VARCHAR(10) utf8mb4,一个字符4字节,加上长度标识,约40字节
  • create_time是DATETIME,5字节

如果EXPLAIN显示key_len=40,说明只用了status列。如果key_len=45,说明两列都用了。


真实案例:一条慢查询的优化全过程

慢查询日志里有一条SQL执行了8秒:

sql

SELECT * FROM logs WHERE user_id = 12345 AND action = 'login' ORDER BY create_time DESC LIMIT 100;

EXPLAIN输出:

  • type: ALL
  • rows: 200万
  • Extra: Using where; Using filesort

分析:全表扫描200万行,还要额外排序,数据量大,自然慢。

加复合索引

sql

ALTER TABLE logs ADD INDEX (user_id, action, create_time);

再EXPLAIN:

  • type: ref
  • rows: 523
  • Extra: 无filesort

查询时间从8秒降到0.02秒。一个索引解决所有问题。


其他常用技巧

查看MySQL是否用了索引下推:EXPLAIN输出Using index condition,说明索引条件下推优化已生效,对减少回表次数有帮助。

查看是否用了覆盖索引:Extra列显示Using index,且没有Using where,说明查询只访问索引,不回表,效率最高。

SHOW WARNINGS:EXPLAIN后执行SHOW WARNINGS,能看到MySQL重写后的SQL,有时能发现隐式类型转换等问题。


最后一句

EXPLAIN是SQL优化的眼睛。用EXPLAIN看执行计划,找到type=ALL,加索引。看到filesort,优化排序。看到rows很大,缩小范围。

下次你遇到慢SQL,不要猜。把EXPLAIN跑出来,看type,看rows,看Extra。三个地方看完,问题通常就找到了。优化后的SQL跑一遍,你会觉得以前的慢得不值。

知识库

服务器文件权限详解:777为什么是灾难?

2026-6-4 18:00:33

知识库

SQL优化实战:3个案例让查询快100倍

2026-6-5 17:41:52

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧