MySql explain执行计划详解

前言

MySql使用explain关键字可以模拟优化器执行sql语句,我们就能够知道MySql会如何处理咱们的sql,

可以根据explain的分析结果和MySql底层数据结构优化sql。文章内容基于MySql 5.7.24分析,

不同MySql版本可能有差别。如果用的是MySql 5.6.X,差别应该不会很大。

MySql索引底层数据结构和算法:https://blog.csdn.net/yhl_jxy/article/details/88392411

一 explain详解

数据脚本准备,有一个teacher(教师表),class(班级表),class_teacher(班级与对应上课老师关系表)。

这里不涉及到实际业务,只是通过实例分析explain相关内容。

# 教师
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
  `id` int(11) NOT NULL comment '教师编号',
  `teacher_name` varchar(45) DEFAULT NULL comment '教师姓名',
  `entry_time` date DEFAULT NULL comment '入职时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `teacher` (`id`, `teacher_name`, `entry_time`)
VALUES (1,'a','2010-09-22'), (2,'b','2011-10-26'), (3,'c','2013-12-25');

# 班级
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
  `id` int(11) NOT NULL AUTO_INCREMENT comment '班级编号',
  `class_name` varchar(10) DEFAULT NULL comment '班级名称',
  PRIMARY KEY (`id`),
  KEY `idx_class_name` (`class_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `class` (`id`, `class_name`)
VALUES (1,'class1'),(2,'class2'),(3,'class3');

# 班级教师关系表
DROP TABLE IF EXISTS `class_teacher`;
CREATE TABLE `class_teacher` (
  `id` int(11) NOT NULL comment '主键',
  `class_id` int(11) NOT NULL comment '班级编号',
  `teacher_id` int(11) NOT NULL comment '教师编号',
  `remark` varchar(255) DEFAULT NULL comment '备注',
  PRIMARY KEY (`id`),
  KEY `idx_class_teacher_id` (`class_id`,`teacher_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `class_teacher` (`id`, `class_id`, `teacher_id`, `remark`)
VALUES (1, 1, 1, '一班的1号教师'), (2, 1, 2, '一班的2号教师'), (3, 2, 1, '二班的1号教师');

先用explain关键字执行下,看看能看到啥? 

mysql> explain select * from teacher;

  • id :编号
  • select_type :查询类型
  • table :表名
  • partitions :分区
  • type :类型
  • possible_keys :预测用到的索引
  • key :实际使用的索引
  • key_len :实际使用索引的长度
  • ref :表之间的引用
  • rows :通过索引查询到的数据量
  • filtered :过滤
  • Extra :额外的信息

下面详细分析每一列的含义。

1、id列(重要)

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

id值越大执行优先级越高,id值相同则从上往下执行,id为null最后执行。

mysql> explain select (select id from teacher limit 1) from class;

MySql先执行id为2的表,即先执行子查询从teacher表获取一条数据,然后再执行id为1的表class。

2、select_type列

select_type表示查询类型,分为以下几类查询。
1)simple

简单查询(不包含子查询,不包含关联查询,比如join,union)。

mysql> explain select * from teacher;

2)primary

包含子查询最外层的select。

3)subquery

包含子查询中的子查询。

mysql> explain select (select 1 from teacher where id = 1) from class;

 subquery子查询teacher表,外层select为primary。

4)derived

包含在from子句中的查询。MySql会将结果放在一张临时表中以供查询使用。

mysql> explain select * from (select 1) tmp;

 5)union

在union中第二个及之后的select。

6)union result

从union临时表检索结果的select。

mysql> explain select 1 union select 2;

select 1为primary。select 2为union。

两个的结果合并为union result,供select 检索。

这里也可以顺便看下id列,1,2,null,执行顺序2 --> 1 --> null。

先执行select 2,然后执行select 1,最后执行执行从两个笛卡尔积检索数据。

3、table列

table为访问的表名。根据上面id或select_type实例观察,总结表名的规则。

1)from子查询时,table为子表名或 格式,表示当前查询依赖 id=N 的查询,于是先执行 id=N 的查询。

mysql> explain select * from (select 1) tmp;

 在table列可以看到,表示主查询依赖于是由DERIVED(select_type)+id,这里就是derived2。

2)当有 union 时,UNION RESULT 的 table 列的值为,1和2表示参与 union 的 select 行id。

mysql> explain select 1 union select 2;

  

4、partitions列

如果查询基于分区表,将会显示访问的是哪个区。

5、type列(重要)

type列表示关联类型或访问类型,MySql决定如何查找表中的行,以及查询表数据行记录的大概范围。

type常见类型从最优到最差:system > const > eq_ref > ref > range > index > ALL,

如果你的sql通过explain分析,看到type是All,一般是需要优化了。

一般得保证查询时能达到range级别,如果能做到ref级别,就更好了。

1)null

MySql优化器能够在优化阶段分解查询语句,在执行阶段就不用访问表或索引。

mysql> explain select max(id) from teacher;

2)system,const

system(忽略):只有一条数据的系统表 或 衍生表只有一条数据的主查询才会出现,可以忽略掉,没有太大意义。

cons(偶尔能达到):只能查询到一条数据的SQL,用于primary key 或 unique索引(查询类型与索引类型有关)。

mysql> explain select * from (select * from class where id = 1) tmp;

3)eq_ref

primary key 或 unique key 索引被连接(join)使用 ,最多只会返回一条符合条件的记录。

对于每个索引键的关联查询,返回匹配唯一行数据(有且只有1个,不能多,不能少)。

简单的select 查询不会出现这种type。

mysql> explain select * from class_teacher left join class on class_teacher.class_id = class.id;

这里也可以看到id列都是1,当id列值一样时,从上到下执行表。所以先执行class_teacher表,后执行class表。 

4)ref

相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,

可能会找到多个符合条件的行。

mysql> explain select * from class where class_name = 'class1';

5)range

range使用索引列检索指定范围,where后面是一个范围查询(between and,in ,>, <, >=)。

mysql> explain select * from teacher where id between 1 and 5;

6)index

查询全部索引中的数据,这通常比ALL快一些。

mysql> explain select * from class;

7)ALL

查询全表数据。没有索引,或索引失效,查询全部数据。

mysql> explain select * from teacher;

6、possible_keys列

possible_keys显示可能用到的索引,该列值可能为空,也可能不为空。

1)不为null时

当不为null时,会显示可能用到的索引。

2)为null时

当该列值为null时,表示不会用到索引。当出现这种情况时,看下where条件是否用到了索引字段,

需要分析为什么没用索引或是否需要添加索引。

7、key列(重要)

key列值显示MySql实际使用到的索引。如果没有使用索引,该列值为null。

8、key_len列

MySql索引使用的字节数,通过该值计算出使用了索引中的哪些列。

比如class_teacher表中,有联合索引idx_class_teacher_id,由class_id和teacher_id字段组成。

使用一个索引字段或使用两个索引字段来观察ken_len的值。

mysql> explain select * from class_teacher where class_id = 1;

mysql> explain select * from class_teacher where class_id = 1 and teacher_id = 1;

 从上面sql可以看到,key_len值分别为4和8,这个值是int=4个字节算出来的,

因为我们的class_id和teacher_id都是int类型,所以key_len存的是使用到的索引字段类型字节长度。

第一个查询sql只用class_id索引字段,所以key_len为4,第二个查询sql用到了class_id和teacher_id,

所以key_len计算为8,但是如果字段允许为null,则需要加1。key_len的计算规则如下:

key_len计算规则如下:

1)字符串

char(n):n字节长度;

varchar(n):2字节存储字符串长度,如果是utf-8,则长度 3n + 2;

2)数值类型

tinyint:1字节

smallint:2字节

int:4字节

bigint:8字节  

3)时间类型

date:3字节

timestamp:4字节

datetime:8字节

如果字段允许为 NULL,需要1字节记录是否为 NULL。(这是为什么会比正常计算多1的原因)。

索引最大长度是768字节,当字符串过长时,MySql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引。

9、ref列

这一列显示了在key列记录的索引中,表查找值所用到的列或常量,一般比较常见为const或字段名称。

mysql> explain select * from class_teacher where class_id = 1 and teacher_id = 1;

key列联合索引class_id和teacher_id,使用到两个索引字段,所以为const,const。 

10、rows列(重要)

MySql估计sql要查询出来的数据条数。

mysql> explain select * from class_teacher where class_id = 1;

 MySql预估可能会查询出两条数据,有时候不准。

11、filtered列

filtered的值指返回结果的行占需要读到的行(rows列的值)的百分比。

mysql> select teacher_name from teacher;

MySql explain执行计划详解_第1张图片 

这条sql能查出三条数据。

mysql> explain select teacher_name from teacher where teacher_name = 'a';

 这条sql能查出一条数据,通过explain可以看到filtered为33.33。

即filtered = 1 / 3 * 100/100 = 33.33%,保留两位小数。

但是我换一个条件查询。

mysql> explain select teacher_name from teacher where id = 1;

 这个filtered是100,理论上应该也是33.33,但是因为where 条件 id是主键,或者别的索引列,它直接根据索引查找,就不需要跟

全局比较。如果索引上查找某个值,它人为你只会去查询一条数据,并且你也只能查询出一条数据,所以是100。

12、Extra列(重要)

Extra显示额外信息,就像你做excel一样,最后搞个备注列。备注很多时候很重要,

Extra列也很重要,通常有如下几类重要信息需要关注。

1)Using index

Extra显示Using Index,说明用到了索引,是性能高的表现。一般出现在查询的列被索引列覆盖,

并且where筛选条件是索引的前导列。

mysql> explain select class_id from class_teacher where class_id = 1;


2)Using where

Extra显示Using where,表示没有用到索引,查询的列未被索引列覆盖,where筛选条件非索引的前导列。

mysql> explain select teacher_name from teacher where teacher_name = 'a';


3)Using where Using index

Extra显示Using whre Using index,表示查询的列被索引列覆盖,并且where筛选条件是索引列之一但

不是索引的前导列,说明无法直接通过索引查找查询到符合条件的数据。

mysql> explain select class_id from class_teacher where teacher_id = 1;


4)NULL

Extra显示null,表示查询的列未被索引列覆盖,并且where筛选条件是索引的前导列,说明用到了索引,

但是部分字段未被索引列覆盖,必须通过“回表”来实现,所以不是纯粹地用到了索引,也不是完全没用到索引。

mysql> explain select * from class_teacher where class_id = 1;


5)Using index condition

Extra显示Using index condition与Using where类似,查询的列不完全被索引列覆盖,where条件中是一个前导列的范围。

mysql> explain select * from class_teacher where class_id > 1;


6)Using temporary

Extra显示Using temporaty表示MySql需要创建一张临时表来处理查询。

中间MySql处理过程需要多处理一个临时表,一般这种情况是需要优化处理的。

优化处理一般使用索引优化手段会多些。

在teacher.teacher_name没有索引,如果对该字段去重处理distinct,则MySql会把查询结果集建

临时表,然后再去重处理,中间多了创建临时表的过程,效率低;

mysql> explain select distinct teacher_name from teacher;

咱们在开始建表的时候,做了一个class.class_name建立了idx_class_name索引,

通过class的class_name建立索引类比teacher上没有建立索引的效果。

mysql> explain select distinct class_name from class;

从比较结果知道,name上建立索引的distinct会走索引去重,而不会创建临时表,索引效率高。

7)Using filesort

Extra显示Using filesort表示MySql会对查询结果使用外部索引排序,

而没有按索引次序从表里读取行,没有用索引。此时MySql会根据联接类型扫描所有符合条件的记录,

并保存排序关键字和行指针,然后排序关键字并按顺序检索行信息。MySql绕着弯干这么多事,

效率很低,如果你的order by出现Using filesort,一般需要优化了。

在teacher.teacher_name未创建索引,会扫描teacher整个表,保存排序关键字teacher_name和对应的id,

然后排序teacher_name并检索行记录。

mysql> explain select * from teacher order by teacher_name;

在class.class_name建立了idx_class_name索引,此时查询时Extra是Using index,使用了索引列排序,

效率高效。

explain select * from class order by class_name;

二 explain总结

1、重点关注id、type、key、Extra列,Extra最复杂。

2、explain需要在实践中不断使用,不断体会,才能自己找到精髓,与自己融为一体。

理论+实战才是唯一标准。 

你可能感兴趣的:(#,---MySql进阶,Thinking,In,MySql)