什么是MySQL的执行计划?

执行计划通常是开发者优化SQL语句的第一步。MySQL在解析SQL语句时,会生成多套执行方案,然后内部会进行一个成本的计算,然后通过优化器选择一个最优的方案执行,然后根据这个方案会生成一个执行计划。开发者通过查看SQL语句的执行计划,可以直观的了解到MySQL是如何解析执行这条SQL语句的,然后再针对性的进行优化。执行计划能显示mysql执行sql时的详细执行情况

语法

explain select * from user;

学习执行计划的意义:

了解SQL执行计划的意义就在于我们可以通过执行计划更加清晰的认识到这一条语句,分为了哪几步,有没有用到索引,是否有一些可优化的地方等。

下面开始一项项说。

实际运行一个EXPLAIN时候,我们都能看到下面的这个表头,下面我们一项项的来说明

MySQL执行计划中的ref列 mysql执行计划在哪生成_执行计划

执行计划中各字段的解释

一 id  (select查询的序列号)

包含一组数字,表示查询中执行select子句或操作表的顺序

三种情况:

1、id相同:认为是同一组,执行顺序由上至下

MySQL执行计划中的ref列 mysql执行计划在哪生成_字段_02

2、id不同:如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行

MySQL执行计划中的ref列 mysql执行计划在哪生成_mysql 生成执行计划_03

3、id相同又不同(两种情况同时存在):id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

MySQL执行计划中的ref列 mysql执行计划在哪生成_MySQL_04

id为null的情况:

比较少见,id为null的部分一定是最后执行的

假如2有两张表

user 表:

CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

address表:

CREATE TABLE `address` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL,
`address` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

表数据如下:

user表:

idusernamepassword

1

p1

123

2

p2

456

address表:

iduser_idaddress

1

1

邯郸

2

2

德州

接下来例子会用到

二  select_type  查询的类型

主要是用于区分普通查询、联合查询、子查询等复杂的查询

序号

select_type

描述

举例

1

SIMPLE

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

EXPLAIN SELECT * FROM user WHERE id=1;

2

PRIMARY

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

EXPLAIN SELECT * FROM userWHERE id=(SELECT user_id FROM address WHERE id=1);

3

SUBQUERY

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

EXPLAIN SELECT * FROM userWHERE id=(SELECT user_id FROM address WHERE id=1);

4

DERIVED

from字句中包含的查询(衍生查询)

EXPLAIN SELECT * FROM (SELECT * FROM user WHERE id>1) t;

5

UNION

出现在union后的查询语句中(当前执行计划的中间记录就是接下来的 UNION RESULT))

EXPLAIN SELECT * FROM user WHERE id=1 UNION SELECT * FROM user WHERE id=2;

6

UNION RESULT

从UNION中获取结果集当前执行计划的最后一条记录就是 UNION RESULT)

EXPLAIN SELECT * FROM user WHERE id=1 UNION SELECT * FROM user WHERE id=2;

MySQL执行计划中的ref列 mysql执行计划在哪生成_SQL_05

三 type  访问类型

sql查询优化中一个很重要的指标,结果值从好到坏依次是:

2、const表示通过索引或者唯一索引一次就找到了,const用于比较primary key 或者 unique索引。因为只需匹配一行数据,所有很快。如果将主键置于where列表中,mysql就能将该查询转换为一个const

MySQL执行计划中的ref列 mysql执行计划在哪生成_MySQL_06

3、eq_ref唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键 或 唯一索引扫描。

MySQL执行计划中的ref列 mysql执行计划在哪生成_SQL_07

注意:ALL全表扫描的表记录最少的表如t1表

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

EXPLAIN SELECT id FROM user WHERE username=‘p1’;

5 range 只检索给定范围的行,

使用一个索引来选择行。key列显示使用了那个索引。一般就是在where语句中出现了bettween、、in等的查询。这种索引列上的范围扫描比全索引扫描要好。只需要开始于某个点,结束于另一个点,不用扫描全部索引

MySQL执行计划中的ref列 mysql执行计划在哪生成_SQL_08

6 index    全索引扫描 ,index与ALL区别为index类型只遍历索引树。这通常为ALL块,应为索引文件通常比数据文件小。(Index与ALL虽然都是读全表,但index是从索引中读取,而ALL是从硬盘读取)

MySQL执行计划中的ref列 mysql执行计划在哪生成_执行计划_09

7 ALL 全表扫描以找到匹配的行

MySQL执行计划中的ref列 mysql执行计划在哪生成_字段_10

四  possible_keys

查询涉及到的字段上存在索引,则该索引将被列出,但不一定被查询实际使用

五 key

实际使用的索引,如果为NULL,则没有使用索引。

查询中如果使用了覆盖索引,则该索引仅出现在key列表中

MySQL执行计划中的ref列 mysql执行计划在哪生成_执行计划_11

MySQL执行计划中的ref列 mysql执行计划在哪生成_mysql 生成执行计划_12

六  key_len

表示索引中使用的字节数,查询中使用的索引的长度(最大可能长度),并非实际使用长度,理论上长度越短越好。

key_len是根据表定义计算而得的,不是通过表内检索出的,

长度也和字符编码有关系,latin1字符占1个字节 ,一个GBK字符占用2个字节,一个UTF8字符占用3个字节

字符类型

序号

字段类型

是否允许为null

字符编码

key-lenth

原因

1

char(10)

不许为null

utf8

30

10*3

2

char(10)

允许为null

utf8

31

null需要1个字节的额外空间

3

varchar(10)

不许为null

utf8

32

变长字段需要额外的2个字节

4

varchar(10)

允许为null

utf8

33

(10*3+2+1)变长字段需要额外的2个字节,null需要1个字节额外空间

数值类型

tinyint  1字节

smallint  2字节

int          4字节

时间类型  datetime

5.6版本中   6字节

5.5版本及之前  8字节

七  ref

显示索引的哪一列被使用了,如果可能,是一个常量const。

八  rows

根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数

九  Extra

不适合在其他字段中显示,但是十分重要的额外信息

1、Using filesort :文件排序

mysql对数据使用一个外部的索引排序,而不是按照表内的索引进行排序读取。也就是说mysql无法利用索引完成的排序操作称为“文件排序”

出现了Using filesort 需要做优化

MySQL执行计划中的ref列 mysql执行计划在哪生成_mysql 生成执行计划_13

由于索引是先按email排序、再按address排序,所以查询时如果直接按address排序,索引就不能满足要求了(此处理解最左优先匹配即可),mysql内部必须再实现一次“文件排序”

2、Using temporary:使用了临时表

使用临时表保存中间结果,也就是说mysql在对查询结果排序时使用了临时表,常见于order by 和 group by

出现了Using temporary 需要做优化

MySQL执行计划中的ref列 mysql执行计划在哪生成_字段_14

3、Using index:  (使用了覆盖索引)

表示相应的select操作中使用了覆盖索引(Covering Index),避免了访问表的数据行,效率高,出现using index 说明SQL还不错

如果同时出现Using where,表明索引被用来执行索引键值的查找(参考上图)

如果没用同时出现Using where,表明索引用来读取数据而非执行查找动作

MySQL执行计划中的ref列 mysql执行计划在哪生成_mysql 生成执行计划_15

覆盖索引(Covering Index):也叫索引覆盖。就是select列表中的字段,只用从索引中就能获取,不必根据索引再次读取数据文件,换句话说查询列要被所建的索引覆盖。

注意:

a、如需使用覆盖索引,select列表中的字段只取出需要的列,不要使用select *

b、如果将所有字段都建索引会导致索引文件过大,反而降低crud性能

4、Using where :

使用了where过滤

5、Using join buffer :

使用了链接缓存

6、Impossible WHERE:

where子句的值总是false,不能用来获取任何元祖

MySQL执行计划中的ref列 mysql执行计划在哪生成_字段_16