MySQL -- Explain执行计划基本介绍及栗子
Posted 做猪呢,最重要的是开森啦
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL -- Explain执行计划基本介绍及栗子相关的知识,希望对你有一定的参考价值。
使用explain可以模拟优化器执行SQL语句,从而知道mysql是如何处理你的SQL语句的。
0. 表创建
CREATE TABLE `actor` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`film_id` int(11) DEFAULT NULL,
`name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_film_id` (`film_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
insert into `actor`(`id`,`film_id`,`name`,`update_time`) values (1,1,'actor1','2021-05-04 17:14:44'),(2,2,'actor2','2021-05-04 17:17:18'),(3,3,'actor3','2021-05-04 17:17:18'),(4,4,'actor2','2021-05-04 17:17:18');
CREATE TABLE `film` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`remark` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
insert into `film`(`id`,`name`,`remark`) values (1,'film1',NULL),(2,'film2',NULL),(3,'film3',NULL);
1. id列
- id列的编号是 select 的序列号,一般有几个 select 就有几个id
- id的顺序是按 select 出现的顺序增长的
- id列越大执行优先级越高,越先执行
- id相同则从上往下执行,id为NULL最后执行
EXPLAIN SELECT NAME FROM actor WHERE film_id = (SELECT id FROM film WHERE NAME = 'film2')
先执行后面的子查询(id列越大执行优先级越高,越先执行)
2. select_type 列
- select_type 表示对应行是简单还是复杂的查询
2.1. simple
- 简单查询,查询不包含子查询和union。
2.2. primary
- 复杂查询(包含子查询或union)中最外层的 select,就会被标记primary;如下查询actor时,就是primary
2.3. subquery
- 包含在 select或where 中的子查询(不在 from 子句中);像上图子查询的film表
2.4. derived
- 包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表(derived的英文含义);见下方union result
2.5. union
- 第二个select出现在union之后,则被标记union
2.6. union result
- 从 union 临时表检索结果的 select
3. table列
- 表示 explain 的一行正在访问哪个表
- 当 from 子句中有子查询时,table列是 格式,表示当前查询依赖 id=N 的查询,于是先执行 id=N 的查询。
- 当有 union 时,UNION RESULT 的 table 列的值为<union2,3>,2和3表示参与 union 的 select 行id。
4. type列(重要)
- 这一列表示关联类型或访问类型,即MySQL决定如何查找表中的行,查找数据行记录的大概范围。
- 常见的依次从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL
- 一般来说,得保证查询达到range级别,最好达到ref
4.1. NULL
- mysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引。
- 例如:在索引列中选取最小值,可以单独查找索引来完成,不需要在执行时访问表
4.2. const和system
- const表示通过索引一次就能找到,用于 primary key 或 unique key 的所有列与常数比较时
- system是const的特例,表里只有一条数据,匹配时为system,可忽略
4.3. eq_ref
- 唯一索引扫描,简单的 select 查询不会出现这种 type。
- primary key 或 unique key 索引键作为关联条件 ,表中只有一条记录与之匹配。
4.4. ref
- 不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。
film的name是普通索引
4.5. range
- 范围扫描通常出现在in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行。
4.6. index
- 扫描全表索引,这通常比ALL快一些。(index是从索引中读取的,而all是从硬盘中读取)
- film的name是普通索引,id是主键索引,通过索引就能获取这两个字段值
- 如果select * ,那么其他字段不能从索引获取,就需要全表扫描
4.7. ALL
- 即全表扫描,意味着mysql需要从头到尾去查找所需要的行。通常情况下这需要增加索引来进行优化了
5. possible_keys列和key列(重要)
- possible_keys这一列显示查询可能使用哪些索引来查找。
- key这一列显示mysql实际采用哪个索引来优化对该表的访问,如果没有使用索引,则该列是 NULL。
理论上用到的索引有两个,但实际用主键索引就够了
- key 显示 NULL 的情况,要么是没建索引,要么是索引失效了。
actor是有主键索引的,这里union就失效了,可以看到possible_keys和key列都为null
6. key_len列(重要)
- 这一列显示了mysql在索引里使用的字节数,通过这个值可以算出具体使用了索引中的哪些列。
- 计算规则:
字符串 char(n):n字节长度
varchar(n):如果是utf-8,则长度 3n + 2 +1,2字节存储字符串长度,允许为null占1字节
tinyint:1字节
smallint:2字节
int:4字节
bigint:8字节
date:3字节
timestamp:4字节
datetime:8字节
允许为null占1字节
主键id不为null,为int类型,所以索引使用到的字节数4
- name为varchar(20),所以最大60,还有2字节存储字符串长度,此外允许为null占1字节
- 所以idx_name索引使用到的字节数为60+2+1=63
7. ref列
- 这一列显示了在key列记录的索引中,表查找值所用到的列或常量
- 常见的有:const(常量),字段名(例:film.id)
8. rows列
这一列是mysql估计要读取并检测的行数,注意这个不是结果集里的行数。
9. Extra列
9.1 Using index
查询的列被索引覆盖,是性能高的表现。一般是使用了覆盖索引(索引包含了所有查询的字段)。
film的name是普通索引,id是主键索引,通过索引就能获取这两个字段值
9.2 Using index condition
当where的字段都是索引字段,并且不符合最左前缀原则时,就会出现Using index condition,可能是索引下推的表现
- 比如table表有a,b,c,d,e个字段,其中a是主键,另外有一联合索引idx_bcd (b,c,d),
- 然后查询select * from table where b=3 and d =5; 执行计划结果Using index condition
9.3. Using where
- 表示mysql服务器将在存储引擎检索行后再进行过滤。
- 许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验
- 因此不是所有带where字句的查询都会显示"Using where"。
9.4 NULL
查询的列未被索引覆盖,并必须通过“回表”来查找,不是纯粹地用到了索引,也不是完全没用到索引
用到了主键索引,但是其他字段不能通过索引获取,需要回表去查找
9.5. Using temporary
需要创建一张临时表来处理查询。常见于排序、去重、分组
9.6. Using filesort
MySQL中无法利用索引完成的排序操作称为“文件排序”
以上是关于MySQL -- Explain执行计划基本介绍及栗子的主要内容,如果未能解决你的问题,请参考以下文章