it编程 > 数据库 > Mysql

MySQL中EXPLAIN分析执行计划

19人参与 2026-08-05 Mysql

一、问题:没有 explain 时,你怎么知道一条 sql 是怎么执行的?

假设你写了一条 sql:

select * from orders where user_id = 100 and status = 'paid';

然后你发现这条 sql 执行很慢。现在你想优化它。

但问题是:你根本看不到 mysql 内部是怎么处理这条 sql 的。

你不知道:

没有 explain 的弊端:

所以 mysql 需要一个工具,把优化器"最终决定的执行方案"暴露出来,让开发者看得见、能分析。

这就是 explain 的设计初衷。

二、explain 是什么?

explain 不会真正执行你的 sql(除了 explain analyze 这种特殊形式)。它只是让 mysql 的查询优化器生成执行计划,然后把计划的各个维度展示给你。

核心用法:

explain select * from users where id = 1;

mysql 会返回一张表格,每一行代表优化器对某个表(或某个步骤)的执行计划。

三、输出字段详解:每个字段解决什么问题?

字段解决什么问题重点关注
id多表查询时,哪个步骤先执行、哪个后执行id 相同从上到下执行,id 越大越先执行
select_type这是什么类型的查询?简单查询?子查询?union?simple(简单)、subquery(子查询)等
table当前这一步操作的是哪张表定位问题表
type怎么访问数据? 这是最重要的字段见下方详细讲解
possible_keys优化器觉得"可能能用"的索引有哪些看有没有你期望的索引
key优化器实际选了哪个索引null 表示没走索引
key_len实际使用了索引的多少字节判断联合索引是否被完全利用
ref索引是和常量比,还是和另一张表的列比const 表示和固定值比
rows优化器预估要扫描多少行越小越好
filtered经过 where 条件过滤后,预计剩下多少百分比的数据百分比越高说明索引过滤效果好
extra有没有额外的"坏操作",比如排序、临时表见下方详细讲解

四、type 字段:从"好"到"差"的完整谱系

type 告诉你 mysql 以什么方式访问数据。这是 explain 里最需要关注的字段。

type 值含义为什么好/坏
system表只有一行数据(几乎见不到)最优
const通过主键或唯一索引,直接定位到一行最优,只读一次
eq_refjoin 时,被驱动表通过主键/唯一索引关联很好,每次只匹配一行
ref通过普通索引(非唯一)等值查询好,可能匹配多行
range索引范围扫描,比如 between、>、<较好,只扫描索引的一部分
index全索引扫描——遍历整个索引树较差,虽然比全表扫描快,但仍要遍历所有索引项
all全表扫描——遍历整个数据文件最差,性能瓶颈的常见原因

看到 type = all,基本意味着你的查询没有走索引,需要重点优化。

五、extra 字段:暴露"隐藏的性能杀手"

extra 列会告诉你优化器有没有做一些"额外的高成本操作"。

extra 值含义为什么需要注意
using index覆盖索引——查询的列都在索引里,不需要回表查数据行好事,减少了一次回表 io
using where存储引擎把数据返回给 server 层后,server 再用 where 条件过滤一般正常,但如果配合 type=all 就很差
using temporary需要创建临时表来存储中间结果坏事,常见于 group by 或 distinct 没有走索引
using filesort需要额外的排序操作,而不是利用索引的有序性坏事,常见于 order by 字段没有索引
using index condition索引下推(icp)——把 where 的一部分判断下推到存储引擎层好事,减少回表次数

看到 using temporary 或 using filesort,通常意味着你的索引设计或 sql 写法有问题。

六、实战分析:用 explain 诊断问题

案例 1:发现全表扫描

explain select * from users where name = 'alice';

输出:

+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows  | extra       |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+
|  1 | simple      | users | all  | null          | null | null    | null | 10000 | using where |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+

诊断:

结论: 需要给 name 字段加索引。

案例 2:验证索引是否生效

-- 给 name 加了索引后再次执行
explain select * from users where name = 'alice';

输出:

+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | extra       |
+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
|  1 | simple      | users | ref  | idx_name      | idx_name | 1023    | const |    1 | using where |
+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+

诊断:

结论: 索引生效,优化成功。

案例 3:联合索引的最左前缀问题

-- 索引是:idx_age_name(age, name)
explain select * from users where name = 'alice';

输出:

+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows  | extra       |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+
|  1 | simple      | users | all  | null          | null | null    | null | 10000 | using where |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+

诊断:

为什么? 因为联合索引要求"最左前缀"匹配。你的查询条件只有 name,缺少最左边的 age,所以 mysql 无法使用这个索引。

结论: 查询条件必须包含联合索引的最左列,或者调整索引顺序为 idx_name_age。

案例 4:覆盖索引(不需要回表)

-- 索引是:idx_age_name(age, name)
explain select name from users where age = 20;

输出:

+----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key          | key_len | ref   | rows | extra                    |
+----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+
|  1 | simple      | users | ref  | idx_age_name  | idx_age_name | 5       | const |  100 | using index              |
+----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+

诊断:

为什么好? 因为你要查的 name 也在索引 idx_age_name 里,mysql 直接读索引就能得到结果,不需要再根据索引里的指针回表去数据页查整行数据。减少了一次磁盘 io。

七、explain 的进阶用法

1. 看更详细的成本估算(mysql 5.7+)

explain format=json select * from users where id = 1;

输出 json 格式,包含更细粒度的成本信息。

2. 实际执行并看真实耗时(mysql 8.0.18+)

explain analyze select * from users where id = 1;

普通 explain 只是"预估"计划,而 explain analyze 会真正执行 sql,并告诉你每一步实际花了多少时间、读了多少行。

八、总结:设计的逻辑链条

步骤设计逻辑
问题sql 性能差,但优化器怎么执行是完全的黑盒
弊端不知道有没有走索引、不知道扫描了多少行、不知道有没有额外排序或临时表,优化只能靠猜和试错
方案explain 把优化器生成的执行计划以表格形式暴露出来
核心字段type(怎么访问数据)、key(实际用了哪个索引)、rows(扫描行数)、extra(有没有额外的高成本操作)
判断逻辑先看 type 是不是 all(全表扫描)→ 再看 key 是不是 null(没走索引)→ 再看 extra 有没有 using temporary / using filesort
结果开发者能精准定位性能瓶颈,是缺索引、索引没用上、还是 sql 写法有问题

所以,explain 的本质是:mysql 给开发者提供的一个"执行计划透视器",让你从"黑盒猜谜"变成"白盒诊断"。

到此这篇关于mysql中explain分析执行计划的文章就介绍到这了,更多相关mysql explain执行计划内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

您想发表意见!!点此发布评论

推荐阅读

MySQL语法库的操作大全

08-05

浅谈MySQL日志分析

08-05

MySQL路由器的具体使用

08-05

MySQL连接错误2003与1045的排查与问题解决

08-05

mysql慢日志的实现示例

08-05

MySQL数据库如何快速查找表中零日期记录

08-05

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论