MySQL Explain详解+实战
文章分为3部分,根据自己需求查看。本文MySQL版本:5.7.38
- Explain使用
- Explain执行计划的每个字段详解
- Sql实战分析执行计划
1. Explain使用
MySQL中的Explain语法很简单,只需要在select
语句前面加上explain
关键字即可。比如现在有一条查询语句
select * from user1;
那么他的执行计划就是
explain select * from user1;
来简单看下输出结果吧
2. Explain执行结果的字段详解
从第一部分的执行结果可以看到执行结果共有12个字段。下面介绍下12个字段的详细解释
-
id:Sql可以分为单表查询、多表查询、子查询、联合查询这几种。这里的ID,一般情况下每个ID对应一个SELECT关键字。如果是联表查询,那么一个ID可能重复出现,第一个出现的就是驱动表,第二个出现的是被驱动表
-
select_type 执行的SQL类型,MySQL将执行语句分为好多种
-
SIMPLE
:单表查询的select_type都是SIMPLE类型,连表查询也是SIMPLE,这是因为MySQL把联表分为两步单表查询。 -
PRIMARY
:如果有嵌套查询,或者大查询(比如UNION)最外层的SELECT(或者第一个SELECT)会被标记为该类型 -
UNION
:使用了UNION关键字就会被标记为该类型,比如下面这个查询explain select * from user union all select * from user;
-
UNION RESULT
:一般出现在不带ALL的UNION语句中 -
SUBQUERY
:如果优化器不能把子查询转化为semi-join(半连接)的形式,并且是不想关子查询,查询类型就会是这个。 -
DEPENDENT SUBQUERY
:如果优化器不能把子查询转化为semi-join(半连接)的形式,并且是相关子查询会出现该类型 -
DEPENDENT UNION
再在包含UNION
或者UNION ALL
的大查询中,如果各个小查询都依赖于外层查询的话,那除了最左边的那个小查询之外,其余的小查询的select_type
的值就是DEPENDENT UNION
-
DERIVED
:派生表会出现该类型explain select * from (select count(1) from user) as a;
-
MATERIALIZED
:对于子查询,优化器采用了物化表的方式与外层查询进行连接查询。 -
UNCACHEABLE SUBQUERY
:一般遇不到 -
UNCACHEABLE UNION
:一般遇不到
-
-
table:每个单表查询都要查的那张表就会记录在该字段中
-
partitions:分区相关的。一般用不到。
-
type:衡量SQL执行效率的重要指标,执行效率从好到坏依次是:system->const->eq_ref->ref->range->index->all 其中const和eq_ref的意思差不多,都是指用到了唯一索引或者主键。区别是const指的是单表查询。eq_ref指的是非单表查询。
-
possible_keys:可能会用到的索引(既然都是可能了,也没啥子必要看了)
-
key:实际用到的索引列
-
key_len:实际用到的索引列的长度,按字节统计(个人感觉也没啥子大用)
-
ref:当使用索引查询时(type不是all),这个字段展示的就是与索引列匹配的值是啥。比如
-
explain select * from user where id = 2;
ref列展示的是const,表示id匹配的是个常量
-
rows:预计扫描的行数,和索引统计有关。
-
filtered:(预计扫描的行数/实际结果集的行数)*100%就是该列的值
-
Extra:比较重要!常见的值有
-
using index
-
using index condition
-
using where
-
using join buffer (Block Nested Loop)
-
using filesort:不能容忍!使用了文件排序,效率会很差。一般是由于
order by
的字段没有索引导致的。 -
using temporary:不能容忍!使用了临时表,效率会很差。一般是由于
group by|distinct|union
的字段没有索引导致的。比如下面这个SQLexplain select distinct username from user ;
-
如果我们关注SQL的执行效率可以关注type字段,该字段只要不是ALL全表扫描,那就代表使用了索引。
3. 实战SQL
先准备一张表:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) DEFAULT NULL,
`password` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`birthday` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `age__index` (`age`),
KEY `user_age_birthday_index` (`age`,`birthday`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4
表中一个主键ID,一个二级索引age,一个联合索引(birthday,age),其他列都是普通列
Extra列的实战
Extra中出现的提示信息有的是可以接受的有的不可以接受,一般来说,出现了using temporary与using filesort都是不太好的征兆。因为会涉及到大量的内存&磁盘IO操作。
Extra列using where
MySQL查询是由两个组件完成的:1是服务层;2是存储引擎层。
当extra出现了这个提示,意味着存储引擎层返回的数据需要在服务层过滤。比如下面这个SQL
explain select * from user where username = '11';
因为username不是索引,所以不会被存储引擎过滤掉。会进行全表扫描,然后把数据然会给服务层,服务层进行过滤掉不符合条件的数据。所以这里的Extra会展示using where。以后碰见using where知道咋回事了把,就是服务层进行工作啦呗。
啥?你不知道服务层和存储引擎层是啥?为啥数据不在存储引擎层过滤?推荐看小孩子的书《MySQL是怎样运行的——从根上理解MySQL》。并非广告,只是它对我的帮助确实很大,希望不懂得同学也可以入坑。
Extra列using index
当查询使用到了普通索引就会出现这个提示。比如下面这个SQL
explain select age from user;
你看,我连where条件都没加,怎么走的索引呢?这既是索引存储的知识了。innodb存储引擎会为每个索引建立一颗B+树。上面这个SQL虽然没有过滤条件,但是它只查询age一个字段,刚好这个字段是建了索引的。所以age的数据都存在age_index这个索引树中。无需回表就能完成查询。
出现了该提示意味着SQL走了索引,也是比较好的提示
Extra列using index condition
出现该提示表示innodb使用了索引下推(什么是索引下推自行搜索)
explain select * from user where age>23 and birthday = '12';
这主要是因为表中有一个联合索引(birthday,age)
那么查询语句的过滤条件使用了这两个字段就像上面这个SQLwhere age>23 and birthday = '12'
,就会出现using index condition的提示。索引下推是MySQL5.6的新功能,默认开启。
有同学疑问了,难道不是using index才对吗?索引就是这俩字段咋还来一个下推?什么是下推?
这要从MySQL5.6之前的版本说起,如果是5.6之前的版本,即使有联合索引并且where条件中使用了联合索引的字段并且满足最左前缀规则,但是MySQL依旧只会使用到第一个字段,然后把数据传到上层(服务层)去筛选数据。这里的下推就是指服务层不再参与联合索引的筛选工作了。把这个工作下推给了存储引擎。
什么?难道这不是存储引擎本就应该做的事吗?这就是一个产品的升级更新了,是产品总会有不足,这个缺点还好现在已经不存在了。而这个特性就是索引下推
Extra列using filesort
当order by
中的字段没有索引,但又需要排序时,就会出现该提示,比如下面这个SQL
explain select * from user order by username;
为什么会出现这个提示呢?要知道innodb存储引擎对于索引的存放是顺序存储的,但是没有索引,可就是乱放一通了。就好像字典里的字,如果不给你前面的索引目录,找到一个字得查遍整个字典,这是很耗时的操作。
所以如果出现该提示,一定要优化!
Extra列using temporary
出现该提示是由于group by|distinct|union
的字段没有索引导致的,比如下面的SQL
explain select distinct username from user ;
explain select count(1) from user group by username;
-- 这里为了方便,使用user表当两张表用了
explain select * from user union select * from user;
其中都会涉及到大量的IO操作。是很费力的, 如果出现该提示也一定要优化!
type实战
以下分析还是基于user表
const
第一种:根据主键ID查询user表
explain select * from user where id = 1;
第二种:根据唯一索引查询user表(这里就不举例了)
eq_ref
explain select * from user a left join user b using(id);
ref
使用普通索引进行等值查询时type字段就是ref
explain select * from user where age = 1;
range
使用普通索引进行范围查询时type字段就是ref
explain select * from user where age > 1;
index
使用到了普通索引,但没办法进行等值匹配或者范围匹配,这种情况通常出现在联合索引中。比如(age_birthday)
索引,如果只是用birthday字段进行查询,由于不满足最左前缀规则,所以只能全表扫描索引树(这里的全表扫描指的是扫描索引树哦)
explain select * from user where birthday = '1222';
all
第一种:当列值没有索引就会是全表扫描 第二种:当全表扫描的代价少于使用索引时,也会走全表扫描(数据量少的情况,数据量大的情况下一般都会走索引)
转载自:https://juejin.cn/post/7190595503640281145