保姆级的MySQL执行计划(Explain)解读

什么是执行计划

在查询语句开头添加explain关键字,然后执行查询,就可以看到执行计划。执行计划代表这个SQL执行的过程,MySQL解释器将如何处理该SQL,通过对执行计划的分析,方便做SQL优化。

数据准备

当前数据库版本为8.0.42

用到的表信息如下:

查询计划解读

下面针对执行计划的每个字段进行举例和说明:

id

id列的编号是 select 的序列号,有几个 select 就有几个id,并且id的顺序是按 select 出现的顺序增长的。

id=1的表是驱动表。

select_type

select_type 查询类型,说明查询的种类,有以下几种:

1.simple 简单查询。查询不包含子查询和union

2.primary:复杂查询中最外层的 select

3.derived:包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表(derived的英文含义)

4.union:在 union 中的第二个和随后的 select

5.union result:从 union 临时表检索结果的 select

6.subquery:包含在 select 中的子查询(不在 from 子句中)

table

这一列表示 explain 的一行正在访问哪个表。

如果有具体表名,则表示读取这个表。例如film,表示对film表进行读取。

如果没有具体表名,则表示对id为对应编号的记录进行操作。例如<union3,4>,表示将id为3和4的两条记录进行union的联合查询。

partitions

说明查询作用在哪个分区表上,如果没有对表进行分区,则为null。

type

这一列表示关联类型或访问类型,即MySQL决定如何查找表中的行。

type的类型有12种,下面举例说明,越靠前的类型是效率越高的类型,也就是说在SQL优化时,尽可能使用下面列出的前面的类型。

标记*号的为常用的类型。

1.system

查询的表只有 0 或 1 行(表的数据库引擎必须是 MyISAM或MEMORY,InnoDB 不行),system效率最高。

2.const *

mysql能对查询的某部分进行优化并将其转化成一个常量。

用于 primary key 或 unique key 的所有列与常数比较时,所以表最多有一个匹配行,读取1次,速度比较快。

3.eq_ref *

primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录。

这可能是在 const 之外最好的联接类型了,简单的 select 查询不会出现这种 type。

直白点说就是一张表的主键字段和另一张表的外键进行关联,就是eq_ref类型。

4.ref *

相比eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。

5.fulltext

全文索引,使用极少。

6.ref_or_null *

类似ref,但是可以搜索值为NULL的行。

当检索的数据包含null时,对应类型为ref_or_null。

7.index_merge

使用极少。

8.unique_subquery

使用极少。

9.index_subquery

使用极少。

10.range *

范围扫描,通常出现在 in(), between ,> ,<, >= 等操作中。

使用一个索引来检索给定范围的行。

range的执行效率不确定,会根据不同的查询条件或高或低。比如ID>1,效率就低,因为要在很多条数据中检索。如果换成ID<10,效率就比较高,因为只需要扫描前面9条记录。

11.index *

基于索引进行全表扫描,和ALL差不多,不同点就是mysql只需扫描索引树,这通常比ALL快一些。

12.ALL *

即全表扫描,意味着mysql需要从头到尾去查找所需要的行。

通常情况下这需要增加索引来进行优化了。

possible_keys

这一列显示查询可能使用哪些索引来查找。

key

key列显示mysql实际采用哪个索引来优化对该表的访问。

key_len

这一列显示了mysql在索引里使用的字节数,通过这个值可以算出具体使用了索引中的哪些列。

这里显示4,是因为主键ID为int类型,占用4字节。

ref

这一列显示了在key列记录的索引中,表查找值所用到的列或常量,常见的有:

const(常量),func,NULL,字段名(例:film.id

row

这一列是mysql估计要读取并检测的行数,注意这个不是结果集里的行数。

filtered

是一个百分比的值,代表 (rows * filtered) / 100 ,这个结果将于前表产生交互。

当建立了索引的情况,一般为100,或者是接近100的比较大的一个值。

Extra

这一列展示的是额外信息。有以下一些类型:

distinct

一旦mysql找到了与行相联合匹配的行,就不再搜索了。

Using index

这发生在对表的请求列都是同一索引的部分的时候,返回的列数据只使用了索引中的信息,而没有再去访问表中的行记录。是性能高的表现。

using index也叫索引覆盖,只通过索引字段就可以完成查询。

Using where

mysql服务器将在存储引擎检索行后再进行过滤。

就是先读取整行数据,再按 where 条件进行检查,符合就留下,不符合就丢弃。

代表数据访问效率不高。

using temporary

mysql需要创建一张临时表来处理查询。

出现这种情况一般是要进行优化的,首先是想到用索引来优化。

下面创建一个索引

再次进行查询

添加索引后,查询就不再创建临时表,而是用索引覆盖,提升了效率。

using filesort

采用文件扫描对结果进行计算排序,效率很差。

这里可能会产生疑惑,已经在name字段上创建了索引,为什么排序还是文件排序,没有用到索引呢,这是因为对于排序,只有select 字段 与order by 字段都被索引覆盖是才允许使用Using Index。把查询语句中的*改成name,再看下效果:

这样就用到了索引。

下面再看一种情况,如果要查询多个字段,排序也是多个字段,会是什么情况呢?

同时用name ,update_time两个字段进行查询和排序,又变成了文件排序。

这是因为只有name字段有索引,而update_time字段没有。

下面为name ,update_time建立复合索引。

然后再查询

这次就用到了索引。

注意order by后面的字段顺序,需要与索引字段顺序一致,否则会导致索引失效。

相关推荐
仍然.6 小时前
MYSQL--- 聚合查询,分组查询和联合查询
数据库
一 乐6 小时前
校园二手交易|基于springboot + vue校园二手交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端
啦啦啦_99996 小时前
Redis-0-业务逻辑
数据库·redis·缓存
自不量力的A同学7 小时前
Redisson 4.2.0 发布,官方推荐的 Redis 客户端
数据库·redis·缓存
Exquisite.7 小时前
Mysql
数据库·mysql
全栈前端老曹7 小时前
【MongoDB】深入研究副本集与高可用性——Replica Set 架构、故障转移、读写分离
前端·javascript·数据库·mongodb·架构·nosql·副本集
R1nG8637 小时前
CANN资源泄漏检测工具源码深度解读 实战设备内存泄漏排查
数据库·算法·cann
阿钱真强道8 小时前
12 JetLinks MQTT直连设备事件上报实战(继电器场景)
linux·服务器·网络·数据库·网络协议
逍遥德8 小时前
Sring事务详解之02.如何使用编程式事务?
java·服务器·数据库·后端·sql·spring
笨蛋不要掉眼泪8 小时前
Redis哨兵机制全解析:原理、配置与实战故障转移演示
java·数据库·redis·缓存·bootstrap