文章目录
一. 概念
有了慢查询后,需要对慢查询语句进行分析。一条查询语句经过MySQL查询优化器后,基于各种成本和规则优化后生成一个所谓的执行计划。
这个执行计划揭露了接下来执行查询的方式。比如多表连接查询的顺序是什么,对于每个表采用什么访问方法来具体执行查询等等。
EXPLAIN
语句来帮助我们查看某个查询语句的具体执行计划,我们需要搞懂EXPLAIN
的各个输出项字段的作用,从而可以针对性提升查询语句的性能。
通过EXPLAIN
关键字可以模拟优化器执行SQL查询语句,进而知道MySQL如何处理SQL的。分析查询语句或者表结的性能瓶颈,可以通过EXPLAIN
解析,如:
- 表的读取顺序
- 数据读取操作的操作类型
- 哪些索引会被使用
- 哪些索引实际被使用
- 使用索引实际的字节数
- 表之间的引用
- 每张表有多少行被优化器查询
二. 语法
执行计划的语句:EXPLAIN + SQL语句
EXPLAIN select * from table
除了select
,其余的delete
,update
,insert
,replace
语句前都可以加EXPLAIN
,用来查询语句的执行计划。
三. 详解各字段
先对explain输出各个列的作用大致罗列下:
- id:在一个大的查询语句中每个
select
关键字都对应一个唯一id
。 - select_type:
select *
关键字对应的查询的类型。 - table:表名。
- partitions:匹配的分区信息。
- type:针对单表的访问方法。
- possible_keys:可能用懂啊的索引。
- key:实际用到的索引。
- key_len:实际使用到的索引长度。
- ref:当使用索引列等值查询时,与索引列进行等值匹配对象的信息。
- rows:预估需要扫描的行数。
- filtered:某个表经过搜索条件后过滤剩余记录条数的百分比。
- Extra:额外的信息。
1. id
id列编号是select的序列号。有几个select就有几个id。
-
id相同,执行顺序由上到下
explain select * from a inner join b on a.id = b.id
-
id不同,如果子查询,id的序号递增,id值越大优先级越高,越先执行。
-
id有null最后执行。
explain select * from a union select * from a;
2. select_type
代表对应行是简单还是复杂的查询。
⭐️类型:
-
SIMPLE:简单查询,查询不包含子查询和union。
explain select * from a; explain select * from a inner join b on a.id = b.id;
-
PRIMARY:查询中若包含任何复杂的子部分,最外层查询则被标记为
PRIMARY
。
-
SUBQUERY:select、where之后包含了子查询,在select语句中出现的子查询语句,结果不依赖于外部查询(不在from语句中)。
-
DEPENDENT SUBQUERY:指在select语句中出现的查询语句,结果依赖于外部查询
-
DEPENDENT SUBQUERY:指在select语句中出现的查询语句,结果依赖于外部查询
-
DERIVED:在from列表中包含的子查询被标记为derived(衍生),MySQL会递归执行这些子查询,把结果放在临时表中。
派生表是在一个查询中定义的一个临时表,它由一个子查询生成,但不是作为查询的一部分返回,而是作为外部查询的一部分来引用。派生表通常用在FROM子句中,看起来像是一个普通的表名,但实际上是一个子查询的结果集。
-- 关闭对衍生表合并优化 set session optimizer_switch='derived_merge=off'; -- 查看optimizer_switch参数 show variables like '%optimizer_switch%'; -- explain select * from (select * from a) as t; -- 还原表合并优化 set session optimizer_switch='derived_merge=on';
-
UNION:若第二个select出现在
UNION
后,则被标记为UNION
explain select * from a union select * from a
-
UNION RESULT:
UNION
关键字会将数据结果进行去重,会使用一个临时表,临时表的记录会被标记为UNION RESULT
explain select * from a union select * from a
-
DEPENDENT UNION :当一个
UNION
操作符连接的查询中,至少有一个查询依赖于外部查询的某个值时,这样的查询被称为DEPENDENT UNION
explain select * from a where id in (select id from a union select id from a)
3. table
该行数据属于哪张表。
4. partitions
和分区相关,一般情况下查询语句的执行计划的partitions列的值都是NULL
5. type
执行计划的一条记录代表着MySQL对某个表的执行查询时的访问方法/访问类型
.
其中type
就表明这个访问方法/访问类型,较为重要的一个指标,结果值从最好到最坏排序:
常用的:
system > const > eq_ref > ref > range > index > all
全type的:
system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > all。
一般来说至少达到range级别,最好达到ref。
⭐️类型:
-
system:系统表,少来给你数据,往往不需要磁盘IO。或者当表中只有一条记录并且该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory
-
const:当我们根据主键或者唯一索引列等值匹配时。
explain select * from a where id = 1
-
eq_ref:进行多表连接查询,被驱动表通过主键或唯一索引进行等着查询。
explain select * from a join b on a.id = b.id
-
ref:普通二级索引等值查询。
create index idx_index on a (user_name); explain select * from a where user_name = ''
-
ref_or_null:命中索引时,查询条件除了等值查询,还包含null值查询。
create index idx_index on a (user_name); explain select * from a where user_name = '' or user_name is null;
-
index_merge:查询条件命中多个索引情况下
create index idx_index on a (user_name); create index idx_index2 on a (sex); explain select * from a where user_name = '' or sex = 1;
-
unique_subquery:查询条件包含子查询,并且子查询的列可以进行主键等值匹配。
-
index_subquery:查询条件包含子查询,并且子查询的列可以通过索引进行等值匹配。
-
range:命中索引时,查询某一个范围内的结果。
create index idx_index on a (user_name); explain select * from a where user_name > '张';
-
index:直接在某个索引树上做条件判断,并且不需要回表。
create index idx_index on a (user_name, sex);
-- 虽然没满足左前缀,但可以遍历idx_index的B+Tree,找到sex等于1记录
-- 查询结果的user_name在联合索引上,不用回表,此时就可以用index
explain select user_name from a where sex = 1;
-
all:直接遍历整个聚簇索引。
explain select * from a
6. possible_keys与key
possible_keys代表可能用到的索引。
key代表实际用到的索引。
7. key_len
key_len代表优化器决定使用某个索引执行查询时,该索引记录的最大长度。
计算原理:
对于固定长度类型的索引列来说,它实际占用的存储空间的最大长度为该索引的固定值。对于指定字符集的变长类型的索引列来说
如varchar(10),使用的字符集是utf8(1个字符占用3个字节),那么该列实际占用的最大存储空间是10 * 3 + 2 + 1 = 33 byte。
如果可以存储null值,key_len比不可存储NULL多1个字节。对于varchar来说是长度可变字段,还会有2个字节的空间来存储该字段变长列的实际长度。
如int,为4个字节。
如datetime,为6个字节。
MySQL执行计划中输出key_len列柱用为了让我们区分某个使用联合索引的查询具体用了几个索引。
8. ref
这一列表明了在key列记录的索引中,表查找值所用到的列或常量
- const 或 NULL:
- const: 当查询使用常量作为索引查找的键值时,
ref
列将显示为const
。这通常发生在查询中使用了常量值(如数字或字符串字面量)作为WHERE子句的一部分。 - NULL: 当查询不使用索引或者使用全表扫描时,
ref
列将显示为NULL
。
- const: 当查询使用常量作为索引查找的键值时,
- 表名.列名:
- 当查询使用某个表的列作为索引查找的键值时,
ref
列将显示为<table_name>.<column_name>
。这意味着查询使用了索引查找来定位行。 - 如果查询涉及到多个表并且使用了某个表的列来查找另一个表中的行,则
ref
列将显示为前一个表的列名。
- 当查询使用某个表的列作为索引查找的键值时,
- func:
- 当查询使用函数作为索引查找的键值时,
ref
列将显示为func
。这通常发生在查询中使用了函数(如LOWER()
或CONCAT()
)来处理索引列。
- 当查询使用函数作为索引查找的键值时,
- 索引名称:
- 有时
ref
列也可能包含索引名称,这通常发生在查询使用了特定索引来查找行。
- 有时
9. rows
如果查询优化器决定使用全表扫描方式对某个表执行查询时,执行计划的row列的代表的预计需要扫描的行数。
如果使用索引查询,rows代表预计扫描索引行数。
10. filtered
表示在JOIN操作中,前一个表的行数据被过滤的比例。
11. Extra
Extra列是用来说明一些额外信息的,我们可以通过这些额外信息来更准确的理解MySQL到底将如何执行给定的查询语句
- using index:使用覆盖索引会出现。
- using where:在查找使用索引情况下,需要回表去查询所需数据。
- using condition:查找使用了索引,但需要回表查询数据。
- using index & using where:查找使用了索引,但需要的数据都在索引列中找到,所以不需要回表查询数据