MySQL EXPLAIN 列说明

写在前面

想要进行 MySQL SQL 语句的优化 Explain 是必定要掌握的。应用驱动学习。参考 MySQL 官方文档 :https://dev.mysql.com/doc/refman/8.0/en/explain-output.htmlhtml

SQL 准备 :mysql

DROP TABLE IF EXISTS student;
CREATE TABLE `student` (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  name VARCHAR(30) NOT NULL COMMENT '姓名' ,
  birthday VARCHAR(30) NOT NULL COMMENT '生日' ,
  sex VARCHAR(10) NOT NULL COMMENT '性别' ,
  PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学生表';
INSERT INTO student VALUES
(NULL , '赵雷' , '1990-01-01' , '男') ,
(NULL , '钱电' , '1990-12-21' , '男') ,
(NULL , '孙风' , '1990-05-20' , '男') ,
(NULL , '李云' , '1990-08-06' , '男') ,
(NULL , '周梅' , '1991-12-01' , '女') ,
(NULL , '吴兰' , '1992-03-01' , '女') ,
(NULL , '郑竹' , '1989-07-01' , '女') ,
(NULL , '王菊' , '1990-01-20' , '女');

DROP TABLE IF EXISTS course;
CREATE TABLE course(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
  name VARCHAR(30) NOT NULL COMMENT '课程名称' ,
  teacher_id INT UNSIGNED NOT NULL COMMENT '教师ID' ,
  PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='课程表';
INSERT INTO course VALUES
(NULL , N'语文' , 1) ,
(NULL  , N'数学' , 2) ,
(NULL  , N'英语' , 3);

DROP TABLE IF EXISTS teacher;
CREATE TABLE teacher(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
  name VARCHAR(30) NOT NULL COMMENT '姓名' ,
  PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='教师表';
INSERT INTO teacher VALUES
(NULL , '张三') ,
(NULL , '李四') ,
(NULL , '王五');

DROP TABLE IF EXISTS score;
CREATE TABLE score(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键' ,
  student_id INT UNSIGNED NOT NULL COMMENT '学生ID' ,
  course_id INT UNSIGNED NOT NULL COMMENT '课程ID' ,
  score DECIMAL UNSIGNED DEFAULT 0 COMMENT '成绩' ,
  PRIMARY KEY (id)
)ENGINE = innoDB DEFAULT CHARSET = utf8 COMMENT '成绩表';
INSERT INTO score(student_id, course_id,score) VALUES
(1 , 1 , 80) ,
(1 , 2 , 90) ,
(1 , 3 , 99) ,
(2 , 1 , 70) ,
(2 , 2 , 60) ,
(2 , 3 , 80) ,
(3 , 1 , 80) ,
(3 , 2 , 80) ,
(3 , 3 , 80) ,
(4 , 1 , 50) ,
(4 , 2 , 33) ,
(4 , 3 , 20) ,
(5 , 1 , 76) ,
(5 , 2 , 87) ,
(5 , 3 , 31.5) ,
(6 , 1 , 34) ,
(6 , 2 , 89) ,
(6 , 3 , 98) ;

Explain 输出的列

id :  查询标识符,表示 SQL 语句中执行 select 子句或者是操做的顺序。

1. id 相同时执行顺序从上至下。算法

2. id 不一样时 , 若是是子查询 ,id 的序号会递增, 序号越大的越先执行。sql

3. id 相同,不一样都存在时,id 相同的能够认为是一组查询按从上至下的顺序执行。id 值越大越优先执行。缓存

4. id 为 NULL , 若是行引用其余行的联合结果,则值能够为NULL。在这种状况下,表列显示像<unionM,N>这样的值,以指示该行引用id值为M和N的行的并。服务器

select_type : 查询的类型,有 11 种

1. SIMPLE :简单的查询没有使用 UNION 或者是子查询 。oop

select * from student ;
select * from student where name = '赵雷';

2. PRIMARY : 最外层的查询类型。学习

select * , 
(select name from teacher where id = teacher_id) as teacher_name
from course ;

3. UNION :第二个或者是在 "UNION" 关键字以后的 select 语句。优化

select * from course
left join score
on course.id = score.course_id
where course.name = '语文'
union
select * from course
left join score
on course.id = score.course_id
where course.name = '数学' ;

4. DEPENDENT UNION :第二个或者是在 "UNION" 关键字以后的 select 语句,依赖外层查询。spa

5. UNION RESULT :两个查询结果集合并后的结果集。

select * from course
left join score
on course.id = score.course_id
where course.name = '语文'
union
select * from course
left join score
on course.id = score.course_id
where course.name = '数学' ;

6. SUBQUERY :子查询中第一个 select 语句。

select * ,
(select name from teacher where id = 2) as teacher_name
from course ;

7. DEPENDENT SUBQUERY : 子查询中第一个 select 语句,依赖外层查询。

select * ,
(select name from teacher where id = teacher_id) as teacher_name
from course ;

8. DERIVED :  衍生表。

9. MATERIALIZED :具体的子查询。

10. UNCACHEABLE SUBQUERY :没法缓存结果的子查询,必须对外部查询的每行从新进行评估。

11. UNCACHEABLE UNION : 第二个或者是在 "UNION" 关键字以后的 select 语句 , 没法缓存结果的子查询。

 

table : 对应行正在访问哪个表,表名或者别名

关联优化器会为查询选择关联顺序,左侧深度优先当 from 中有子查询的时候,table 是 derivedN 的形式, N 指向子查询,就是 explain 结果中的那一列。当 select_type 是 union result 的时候 ,table 是 “union 1 ,2” 这样的形势 ,1 ,2 表示 explain 结果中的 id 。注意:MySQL 对待这些表和普通表同样,可是这些“临时表”是没有任何索引的。

select * from course
left join score
on course.id = score.course_id
where course.name = '语文'
union
select * from course
left join score
on course.id = score.course_id
where course.name = '数学' ;

最后一行的 table 是 <union1,2> ,  这里的 1 和 2 ,就是图中 id = 1 和 id = 2 的行。

partitions :将由查询匹配记录的分区。未分区表的值为NULL。

type :对表的访问类型 , 有 12 种类型从最好到最糟的顺序以下 :

system :这个表中只有一条记录,这是 const 类型的特殊状况。

const : 该表最多有一个匹配行,它在查询开始时被读取。由于只有一行,因此该行中的列的值能够被优化器的其他部分视为常量。const表很是快,由于它们只读一次。

像是这种用主键或者惟一性索引可以惟必定位一条记录的就是 const 类型。

select * from student where id = 2 ;

eq_ref : 最多只返回一条符合条件的记录 ,当使用主键或者是惟一性非NULL索引时产生。

ref : 一种索引访问,返回全部匹配某个单个值的行。此类索引访问只有当使用非惟一性索引或惟一性索引非惟一性前缀时才会发生。这个类型和eq_ref 不一样的是,它用在关联操做只使用了索引的最左前缀,或者索引不是惟一的和主键索引。ref 能够用于使用 < ,= ,> 操做符带索引的列。

full_text : 使用到了 full text 索引。

ref_or_null : 相似于 ref 可是MySQL对包含 NULL 值的行进行了特殊的搜索。

index_merge :  表示使用到了索引合并优化,在这种状况下,输出行中的键列包含使用的索引列表,key_len包含所使用的索引的最长键部分的列表。

unique_subquery : 这种类型取代了 eq_ref 在一些子查询中,相似 :value IN (SELECT primary_key FROM single_table WHERE some_expr) 。

index_subquery :  与 unique_subquery 相似 , 这种类型也是做用于子查询中, 相似 :value IN (SELECT key_column FROM single_table WHERE some_expr) 。

range : 只有在指定范围内的行被检索,使用索引来进行查找。explain 结果中的 key 列显示使用了那个索引 。当使用了 = ,< , > , <> , <= , >= , between , like ,  in() 这些操做符的时候产生。

index :全索引扫描 ,扫描全表的时候按照索引的顺序而不是按照行进行扫描。若是索引是查询的覆盖索引,而且能够用于知足表中所需的全部数据,则只扫描索引树。若是没有发生索引覆盖则还须要回行到磁盘上查找数据。

ALL : 在磁盘上对表进行全表扫描。

NULL : MySQL能在优化阶段分解查询语句,在执行阶段甚至用不到访问表或索引(高效)。

possible_keys :  有可能使用到的索引。

key : 实际使用到的索引。

key_len : 使用到的索引所占的字节数。

ref : 显示了以前的表在key列记录的索引中查找值所用的列或常量。

rows : 为了找到所需的行而须要读取的行数,估算值。

filtered : 由表条件过滤的表行的估计百分比。最大值为100,这意味着没有发生行过滤。值从100降低代表过滤量增长。

 

Extra :  额外的信息:

Using filesort : 说明MySQL须要对数据进行额外的排序操做,不能经过索引顺序来进行排序,这个操做比较消耗CPU资源。

Using temporary :  使用了临时表保存中间结果,MySQL在对查询结果排序时使用了临时表。常见于 order by , group by , join 操做。

Using index : 发生了索引覆盖 , 查询时在索引树上取到了须要查询的数据,不须要再进行回行操做。

Using join buffer : 使用了链接缓存:Block Nested Loop,链接算法是块嵌套循环链接;Batched Key Access,链接算法是批量索引链接。

Using where : 使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。注意:Extra列出现Using where表示MySQL服务器将存储引擎返回服务层之后再应用WHERE条件过滤。

distinct : 优化distinct操做,在找到第一匹配的元组后即中止找一样值的动做。