MySQL执行计划EXPLAIN详解


本文以MySQL 5.7 Reference Manual为主轴(翻译&取其精华)并结合网文百家之长整理而成,因为笔者水平有限,文中如有不准确之处请包涵,如转载请注明原文出处guocun09-Oraman的日记

 

基本概念:

EXPLAIN 提供SQL语句是怎么样执行的信息,为select,delete,insert,replace,update语句工作。

EXPLAIN为查询语句中使用到的每个table返回一行信息。

MySQL中所有的join方式都是使用 nested-loop join

 

一.详细说明

EXPLAIN Output Columns

1.select_type

2. type 访问 类型

3.Extra 信息 ( 常用附加信息 )

执行计划包含的信息 id 有一组数字组成。表示一个查询中各个子查询的执行顺序;

  • id相同执行顺序由上至下。

  • id不同,id值越大优先级越高,越先被执行。

  • id为null时表示一个结果集,不需要使用它查询,常出现在包含union等查询语句中。

select_type 每个子查询的查询类型,一些常见的查询类型。

id
select_type description

1

SIMPLE

不包含任何子查询或union等查询

2

PRIMARY

包含子查询最外层查询就显示为 PRIMARY

3

SUBQUERY

在select或 where字句中包含的查询

4

DERIVED

from字句中包含的查询

5

UNION

出现在union后的查询语句中

6

UNION RESULT

从UNION中获取结果集,例如上文的第三个例子

type(非常重要,可以看到有没有走索引) 访问类型

  • ALL   扫描全表数据

  • index 遍历索引

  • range 索引范围查找

  • index_subquery 在子查询中使用 ref

  • unique_subquery 在子查询中使用 eq_ref

  • ref_or_null 对Null进行索引的优化的 ref

  • fulltext 使用全文索引

  • ref   使用非唯一索引查找数据

  • eq_ref 在join查询中使用PRIMARY KEYorUNIQUE NOT NULL索引关联。

possible_keys 可能使用的索引,注意不一定会使用。查询涉及到的字段上若存在索引,则该索引将被列出来。当该列为 NULL时就要考虑当前的SQL是否需要优化了。

key 显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。

TIPS:查询中若使用了覆盖索引(覆盖索引:索引的数据覆盖了需要查询的所有数据),则该索引仅出现在key列表中

key_length 索引长度

ref 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

rows 返回估算的结果集数目,并不是一个准确的值。

extra 的信息非常丰富,常见的有:

  1. Using index 使用覆盖索引

  2. Using where 使用了用where子句来过滤结果集

  3. Using filesort 使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。

  4. Using temporary 使用了临时表 sql优化的目标可以参考阿里开发手册

【推荐】SQL性能优化的目标:至少要达到 range 级别,要求是ref级别,如果可以是consts最好。
说明:
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索。
反例:explain表的结果,type=index,索引物理文件全扫描,速度非常慢,这个index级别比较range还低,与全表扫描是小巫见大巫。

. 实验

环境准备

CREATE DATABASE `gc` /*!40100 DEFAULT CHARACTER SET utf8 */;

use gc;

CREATE TABLE `emp` (

  `emp_no` varchar(20) NOT NULL,

  `emp_name` varchar(30) NOT NULL,

  `age` int(11) DEFAULT NULL,

  `dept` varchar(45) DEFAULT NULL,

  PRIMARY KEY (`emp_no`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into emp values ('MW00001','Oraman',30,'1');

insert into emp values ('MW00002','GC',25,'2');

insert into emp values ('MW00003','Tom Kyte',50,'1');

insert into emp values ('MW00004','Jack Ma',40,'3');

insert into emp values ('MW00005','James',33,'4');

CREATE TABLE `dept` (

  `dept_no` varchar(45) NOT NULL,

  `dept_name` varchar(30) NOT NULL,

  `dept_header` varchar(20) DEFAULT NULL,

  PRIMARY KEY (`dept_no`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into dept values ('1','DBA','MW00003');

insert into dept values ('2','DEV','MW00002');

insert into dept values ('3','BOD','MW00004');

insert into dept values ('4','Business','MW00005');

 

1.

mysql> explain select * from emp where dept='1';

+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |

+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

|  1 | SIMPLE      | emp   | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |    20.00 | Using where |

+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

解释:Simple 简单的单表查询,type:all 全表扫描,Extra:Using where 使用where子句

 

2.

mysql> explain select * from emp where emp_no='MW00001';

+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+

| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |

+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+

|  1 | SIMPLE      | emp   | NULL       | const | PRIMARY       | PRIMARY | 62      | const |    1 |   100.00 | NULL  |

+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+

解释:Simple 简单的单表查询,type: const 使用到PK,possible_keys:可能使用到index为PRIMARY,key:实际使用到index为PRIMARY

 

3.

mysql> explain select * from emp a,dept b where a.emp_name='Oraman' and a.dept=b.dept_no;

+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+

| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref       | rows | filtered | Extra       |

+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+

|  1 | SIMPLE      | a     | NULL       | ALL    | NULL          | NULL    | NULL    | NULL      |    5 |    20.00 | Using where |

|  1 | SIMPLE      | b     | NULL       | eq_ref | PRIMARY       | PRIMARY | 137     | gc.a.dept |    1 |   100.00 | NULL        |

+----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+

解释:两行id相同都是1,以第一行做为驱动表先执行。

Simple 简单的单表查询,第一行type:all 全表扫描,第二行type: eq_ref a表与b表连接使用到= 且只有一行,ref:gc.a.dept 通过a表dept栏位连接b表

 

4.

mysql> explain select * from dept b where exists (select * from emp a where age>30 and a.dept=b.dept_no);

+----+--------------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

| id | select_type        | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |

+----+--------------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

|  1 | PRIMARY            | b     | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |   100.00 | Using where |

|  2 | DEPENDENT SUBQUERY | a     | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |    20.00 | Using where |

+----+--------------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

解释:id为2的做为驱动表第2行先执行,select_type:DEPENDENT SUBQUERY 子查询并依赖外部查询结果集。第1行select_type:PRIMARY 最外层的select

 

以上几个基本的EXPLAIN例子看懂了吗?是不是很简单,和Oracle的区别请自己领悟了。。


请使用浏览器的分享功能分享到微信等