本文以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
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 的信息非常丰富,常见的有:
-
Using index 使用覆盖索引
-
Using where 使用了用where子句来过滤结果集
-
Using filesort 使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。
-
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的区别请自己领悟了。。