explain 的全部字段

需要重点关注的字段

id、type、key、rows、Extra

id字段

select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
三种情况:

1、id相同:执行顺序由上至下,

首先查t3表 得到了所有 t3表的id ,然后查t1表,所有得到所有与t3表id相同的id(交集)以及name字段等于 ‘ ‘的id 再查t2表 ,得到所有与t2表id相同的id

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

先查 t3 再查 t1 在查 t2

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

先查 t3

select_type

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

1、SIMPLE:简单的select查询,查询中不包含子查询或者union (简单查询)
2、PRIMARY:查询中包含任何复杂的子部分,最外层查询则被标记为primary (最外面的一层查询)
3、SUBQUERY:在select 或 where列表中包含了子查询 (where 或者 select 里面的查询)
4、DERIVED:在from列表中包含的子查询被标记为derived(衍生),mysql或递归执行这些子查询,把结果放在零时表里 (from里面的查询)
5、UNION:若第二个select出现在union之后,则被标记为union;若union包含在from子句的子查询中,外层select将被标记为derived
6、UNION RESULT:从union表获取结果的select

type(where)

有没有用到索引主要从这里看(如果走了全表扫描就表示没有用到索引),并且还能看多索引的利用效率。里面的字段就相当于利用索引时的扫描范围

主要看 where 等条件语句 的扫描范围

访问类型,可以简单理解为索引的扫描范围

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

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

往前主要针对于 where 后面的等值查询,往后就是范围查询或者全表查询

1、system:

表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,可以忽略不计,因为优化器不知道这个表里是不是只有一个数据

2、const:

对于主键或者唯一索引进行的等值查询 ,最多只返回一条数据(利用这一点可以出现system但是没有啥意义)

3、eq_ref:

一般出现在联表查询中, 联表查询 where后跟 关联条件,如果关联条件中是某一张表的主键或者唯一索引,就会出现这种情况,如下:t2 表的id字段(主键)作为关联条件出现在where后面

image-20230405153926577

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

4、ref:

当where后的条件 满足最左匹配原则、且不走主键或者唯一非空索引的 就为 ref

一般对于非聚簇索引(二级索引)的等值条件查询都为ref;

非唯一性索引扫描,返回匹配某个单独值的所有行。本质是也是一种索引访问,它返回所有匹配某个单独值的行,然而他可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体

image-20230405154226114

5、ref_or_null:

和ref差不多,只是在其基础上加了 null值查询

explain select * from t1 where name ='t1_name1' or name is null;

由于判断null需要扫描所有索引树种的行信息,索引回避ref 慢

6、index_merge

会将多个搜索结果合并成一个,统一回表,还可以对结果排序

7、range:

索引的列使用了范围查询,要对索引树进行扫描

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

image-20230405154453984

8、index:

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

image-20230405154528967

9、ALL:(不使用索引)

Full Table Scan:

遍历全表以找到匹配的行

image-20230405154600128

也就是未使用索引或者索引失效的情况 一般发生于

1、查询条件使用了 不等于查询 !=以及 <>

2、orin 优势也会导致索引失效

explain SELECT * from employees where name in ('LiLei','abc');

首先 如果使用索引的话,mysql大概会怎么操作? 应该先在name索引树中定位到name=LiLei这个节点(最少一次I/O),然后定位到name=abc这个节点(一次I/O),然后分别拿到主键id,在去主键索引树上扫描定位(最少又要两次I/O),总共4次I/O。

如果不使用索引,直接全表扫描,那么直接扫描主键索引树的叶子节点 只需要两次I/O即可(因为只有两条数据),所以mysql评估全表扫描效率可能会更高,就不会走索引了。

3、is null ,is not null一般情况下也无法使用索引

  • 建表的时候减量避免空字段,如果有可以使用特殊字符代替

4、是用字符串查询 不见引号 索引也会失效(隐式类型转换,还有运算)

5、 范围查找但是查找的范围太大了,有点像上面的第12条,因为要回表之类的很麻烦

  • 优化(一定用索引的话):把一个大的范围拆成多个小的范围 可以利用索引查询。多查几次

6、范围查找 种字符串的不使用前缀匹配

  • like查询建议使用xxx%方式匹配,%xxx或者%xxx%索引失效

KEY

possible_keys

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

如果在进行explain分析sql时,发现这一列有值,但是key列为null,因为mysql觉得可能会使用索引,但是又因为表中的数据很少,使用索引反而没有全表扫描效率高,那么mysql就不会使用索引查找,这种情况是可能发生的。

key

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

PS:

possible_keys为null,key为所建索引的原因查询中如果使用了覆盖索引,则该索引仅出现在key列表中

image-20230405161554775

key_len

表示索引中使用的字节数,查询中使用的索引的长度(最大可能长度),并非实际使用长度,理论上长度越短越好。key_len是根据表定义计算而得的,不是通过表内检索出的

用到的索引字段的长度,通过这个字段可以显示具体使用到了索引字段中的哪些列(主要针对联合索引):计算公式如下

字符串
char(n):n字节长度

     varchar(n):如果是utf-8,则长度 3n + 2 字节,加的2字节用来存储字符串长度

数值类型
tinyint:1字节

     smallint:2字节  

     int:4字节  

     bigint:8字节

时间类型
date:3字节

       timestamp:4字节  

       datetime:8字节

如果字段允许为 NULL,需要增加1字节记录是否为 NULL
索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索
引。

rows

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

Extra

using index (执行索引相关的)

using index

​ 覆盖索引:

​ 想查找的数据都在索引树中,不需要回表再去查询聚簇索引

using index condition

需要回表

​ 索引下推:即 index condition pushdown ICP (mysql5.6+

​ 需要大量回表时,回表前 会处理过滤逻辑 ,下推 到存储引擎当中,过滤后再发到server层进行处理,减少回表次数

using where:

需要回表

​ 没有索引下推的过滤操作,效率相较于using index condition低

总结单看效率,虽然用到了索引,但是效率上 using index > using index condition>using where ,遇到 using where 可以优化优化

using MRR

对二级索引进行范围查询时,会先对查询结果缓存并排序,然后去统一去聚簇索引回表查询

这样能把随机IO 优化为顺序IO提升效率,

using join buffer(联表查询相关)

using join buffer(Block Nested Loop,BNL)

发生在两表关联查询, 关联条件上(JOIN * ON 后面的等值条件),A表的关联列上有索引,B的关联列上没有,就会发生BNL优化

explain select * from test1
		left join test2
		on test1.name (任意索引)= test2.name

从A表中取10行数据放进 join buffer内存空间中,再全表扫描B表和 join buffer中这10行进行关联 然后循环这一步直到所有数据都关联完毕

using join buffer(Batch Key Access ,BKA)

在BNL的基础上,如果B表有索引就配合 using MRR,将 join buffer中的行进行排序 ,再去B表索引中查询,无需全表扫描(默认关闭)

explain select * from test1
		left join test2
		on test1.name (任意索引)= test2.name(任意索引)

using union(indexs)

多个查询条件都是索引(索引合并)

explain select * from test1
		where name = 'name1' (任意索引) or id =1 (任意索引) ;

using temporary(性能很低)

执行过程中用到了临时表

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

explain select count(1) from test 
	group by age;

Using filesort (排序相关,性能很低)

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

一般优化方法是将条件和排序两个字段组成联合索引

这里写图片描述