MySQL中EXPLAIN分析执行计划

 更新时间:2026年08月05日 09:43:13   作者:ruleslol  
EXPLAIN命令是MySQL查询优化的核心工具,用于分析查询执行计划,本文就来详细的介绍一下MySQL EXPLAIN的使用,具有一定的参考价值,感兴趣的可以了解一下

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

假设你写了一条 SQL:

SELECT * FROM orders WHERE user_id = 100 AND status = 'paid';

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

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

你不知道:

  • 它是直接通过索引定位到那几行数据,还是把整张表从头到尾扫了一遍?
  • 你之前给 user_id 建的索引,这次查询到底有没有被用上?
  • 如果有 3 张表 JOIN,MySQL 是先查 A 再关联 B,还是先查 B 再关联 A?
  • 它预估要扫描多少行?有没有做额外的排序或创建临时表?

没有 EXPLAIN 的弊端:

  • 优化完全靠猜——你只能凭经验"觉得"这里该加个索引,但加完也不知道有没有生效。
  • 索引建了白建——你创建了联合索引,但查询条件顺序不对,MySQL 根本没走这个索引,你却不知道。
  • 多表 JOIN 是黑盒——你写了一个复杂的关联查询,但优化器可能选择了一个效率极低的执行顺序,你无从得知。
  • 试错成本极高——改一次 SQL、加一次索引、跑一次测试,循环往复,效率极低。

所以 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 |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+

诊断:

  • type = ALL:全表扫描
  • key = NULL:一个索引都没走
  • rows = 10000:把一万行全扫了一遍

结论: 需要给 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 |
+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+

诊断:

  • type = ref:走了普通索引的等值查询
  • key = idx_name:实际使用了你建的索引
  • rows = 1:只扫描了 1 行

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

案例 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 |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+

诊断:

  • 索引 idx_age_name 在 possible_keys 里都没出现
  • type = ALL,全表扫描

为什么? 因为联合索引要求"最左前缀"匹配。你的查询条件只有 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              |
+----+-------------+-------+------+---------------+--------------+---------+-------+------+--------------------------+

诊断:

  • type = ref:走了索引
  • Extra = 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执行计划内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MYSQL 创建函数出错的解决方案

    MYSQL 创建函数出错的解决方案

    在程序开发过程中,大家有没有遇到过mysql函数不能创建,我是遇到过,是一个很麻烦的问题,上网搜了些相关资料,整理在一起了,供大家参考,帮助那些需要帮助的朋友
    2015-08-08
  • MySQL8.0报错Public Key Retrieval is not allowed的原因及解决方法

    MySQL8.0报错Public Key Retrieval is not allowed的原因及解决方法

    这篇文章主要给大家介绍了MySQL8.0报错Public Key Retrieval is not allowed的原因及解决方法,文中通过代码示例和图文介绍的非常详细,有遇到相同问题的朋友可以参考阅读一下
    2024-01-01
  • MySQL 5.7之关于SQL_MODE的设置

    MySQL 5.7之关于SQL_MODE的设置

    这篇文章主要介绍了MySQL 5.7之关于SQL_MODE的设置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • mysql如何能有效防止删库跑路

    mysql如何能有效防止删库跑路

    本文主要介绍了mysql如何能有效防止删库跑路,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • SQL语句实现多表查询

    SQL语句实现多表查询

    这篇文章主要介绍了SQL语句实现多表查询,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参一下下面文章详细内容
    2022-07-07
  • Mysql中的单表最大记录是多少

    Mysql中的单表最大记录是多少

    这篇文章主要介绍了Mysql中的单表最大记录是多少问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • MySQL ALTER命令使用详解

    MySQL ALTER命令使用详解

    这篇文章主要为大家详细介绍了MySQL ALTER命令的使用方法,简单实用,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • MySQL会发生死锁的几种情况及处理方法

    MySQL会发生死锁的几种情况及处理方法

    数据库的死锁是指不同的事务在获取资源时相互等待,导致无法继续执行的一种情况,当发生死锁时,数据库系统会自动中断其中一个事务,以解除死锁,本文给大家介绍了MySQL什么情况下会死锁,发生了死锁怎么处理呢,需要的朋友可以参考下
    2023-09-09
  • MySql子查询IN的执行和优化的实现

    MySql子查询IN的执行和优化的实现

    本文主要介绍了MySql子查询IN的执行和优化的实现,详细的介绍了为什么IN这么慢以及如何优化,具有一定的参考价值,感兴趣的可以了解一下
    2021-07-07
  • mysql_connect(): Connection using old (pre-4.1.1) authentication protocol refused

    mysql_connect(): Connection using old (pre-4.1.1) authentica

    MySQL错误提示:Connection using old (pre-4.1.1) authentication protocol refused (client option ‘secure_auth’ enabled)解决办法,需要的朋友可以参考下
    2014-04-04

最新评论