MySQL explain 参数解读
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后面
注意:ALL全表扫描的表记录最少的表如t1表
4、ref:
当where后的条件 满足最左匹配原则、且不走主键或者唯一非空索引的 就为 ref
一般对于非聚簇索引(二级索引)的等值条件查询都为ref;
非唯一性索引扫描,返回匹配某个单独值的所有行。本质是也是一种索引访问,它返回所有匹配某个单独值的行,然而他可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体
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等的查询。这种索引列上的范围扫描比全索引扫描要好。只需要开始于某个点,结束于另一个点,不用扫描全部索引
8、index:
Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常为ALL块,应为索引文件通常比数据文件小。(Index与ALL虽然都是读全表,但index是从索引中读取,而ALL是从硬盘读取)不需要回表
9、ALL:(不使用索引)
Full Table Scan:
遍历全表以找到匹配的行
也就是未使用索引或者索引失效的情况 一般发生于
1、查询条件使用了 不等于查询 !=
以及 <>
2、or
、 in
优势也会导致索引失效
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列表中
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无法利用索引完成的排序操作成为“文件排序”,效率低。
一般优化方法是将条件和排序两个字段组成联合索引