Mysql的explain

Sql的编写过程和解析过程

编写过程:

select dinstinct  ...from  ...join ...on ...where ...group by ...having ...order by ...limit ...

解析过程:

from ... on... join ...where ...group by ...having ...select dinstinct ...order by ... limit ...

Mysql中explain

explain :可以模拟SQL优化器执行SQL语句,从而让 开发人员 知道自己编写的SQL状况

比如下边的sql:

EXPLAIN 
select * 
from t_flow_sterilize_batch 
a 
join ssd_dev.t_flow_sterilize_batch_instance 
b 
where a.id = b.sterilize_batch_id

结果如下:

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE b (null) ALL (null) (null) (null) (null) 8 100.0 Using where
1 SIMPLE a (null) eq_ref PRIMARY PRIMARY 130 ssd_dev.b.sterilize_batch_id 1 100.0 (null)

id : 优先级,id值越大越优先;id值相同,从上往下 顺序执行
select_type :查询类型
table :表
type :索引类型
possible_keys :预测用到的索引
key :实际使用的索引
key_len :实际使用索引的长度
ref :表之间的引用,指明当前表所 参照的 字段
rows :通过索引查询到的数据量 filtered:返回结果的行占需要读取的行(rows列的值)的百分比,就是百分比越高,说明需要查询到数据越精确,百分比越小,说明查询到的数据量大,而结果集很少
Extra : 额外的信息

下边对比较重要的字段进行说明:

select_type字段

  1. simple:简单查询(不包含子查询、union)
  2. PRIMARY:包含子查询SQL中的 主查询 (最外层)
  3. SUBQUERY:包含子查询SQL中的 子查询 (非最外层)
  4. derived:衍生查询(使用到了临时表,union)
  5. union:使用了union
  6. union result :告知开发人员,那些表之间存在union查询

在from子查询中, 如果有table1 union table2 ,则table1 就是derived,table2就是union,例如下边:

explain  select  cr.cname  
from ( select * from course where tid = 1  
      union 
      select * from course where tid = 2 ) cr ;

type字段

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

一般用不到上边这么多。

常见的索引类型:system>const>eq_ref>ref>range>index>all ,要对type进行优化的前提:有索引

其中:system,const只是理想情况;实际能达到 ref>range

system(忽略): 只有一条数据的系统表 或 衍生表只有一条数据的主查询

const:仅仅能查到一条数据的SQL,通过索引一次命中 ,用于Primary key 或unique索引

eq_ref唯一性索引:对于每个索引键的查询,返回匹配唯一行数据(有且只有1个,不能多 、不能0),常见于唯一索引 和 主键索引,有可能需要回表

ref非唯一性索引,对于每个索引键的查询,返回匹配的所有行(0,多),有可能需要回表

range:检索指定范围的行 ,where后面是一个范围查询(between ,> < >=, 特殊:in有时候会失效 ,从而转为 无索引 all)

index:查询全部索引中数据,只遍历索引树

all:全表扫描

system/const: 结果只有一条数据
eq_ref:结果多条;但是每条数据是唯一的 ;
ref:结果多条;但是每条数据是是0或多条 ;

key_len字段

作用:用于判断复合索引是否被完全使用

  1. 如果索引字段可以为Null,则会多加1个字节用于标识。
  2. 如果索引字段是可变长度的,会多加2个字节标识可变长度。

不同的编码一个字符的字节数不同:

utf8:1个字符3个字节
gbk:1个字符2个字节
latin:1个字符1个字节

Extra字段

using filesort没有走索引排序,性能消耗大;需要“额外”的一次排序(查询) 。常见于 order by 语句中。

explain select * from test02 where a1 ='' order by a2 ; --using filesort

对于单索引, 如果排序和查找是同一个字段,则不会出现using filesort;如果排序和查找不是同一个字段,则会出现using filesort;

alter table test02 add index idx_a1_a2_a3 (a1,a2,a3) ;
explain select *from test02 where a1='' order by a3 ;  --using filesort
explain select *from test02 where a2='' order by a3 ; --using filesort
explain select *from test02 where a1='' order by a2 ; --没有using filesort
explain select *from test02 where a2='' order by a1 ; --using filesort

上边是复合索引的情况。

小结: where和order by 按照复合索引的顺序使用,不要跨列或无序使用。

using temporary:性能损耗大 ,用到了临时表。一般出现在group by 语句中。

explain select a1 from test02 where a1 in ('1','2','3') group by a1 ; --没有using temporary
explain select a1 from test02 where a1 in ('1','2','3') group by a2 ; --using temporary

避免:查询那些列,就根据那些列 group by

using index :性能提升; 索引覆盖(覆盖索引)。原因:不读取原文件,只从索引文件中获取数据 (不需要回表查询)

例如:test02表中有一个复合索引(a1,a2)

explain select a1,a2 from test02 where a1='' or a2= '' ; --using index
explain select a1,a3 from test02 where a1='' or a3= '' ; --没有索引覆盖

using where需要回表查询

假设age是索引列

但查询语句select age,name from ... where age =...,此语句中必须回原表查Name,因此会显示using where

explain select a1,a3 from test02 where a3 = '' ; --a3需要回原表查询

impossible where : where子句永远为false

explain select * from test02 where a1='x' and a1='y' ; --a1不可能又是x又是y

explain的两种格式

Mysql中explain有两种格式,分别是

  1. format=tree
  2. format=json,返回json格式的explain

举个栗子:

format = tree

EXPLAIN format=tree 
select * 
from t_flow_sterilize_batch a 
join ssd_dev.t_flow_sterilize_batch_instance b 
where a.id = b.sterilize_batch_id

结果为:

-> Nested loop inner join  (cost=3.85 rows=8)
    -> Filter: (b.sterilize_batch_id is not null)  (cost=1.05 rows=8)
        -> Table scan on b  (cost=1.05 rows=8)
    -> Single-row index lookup on a using PRIMARY (id=b.sterilize_batch_id)  (cost=0.26 rows=1)

format = json

EXPLAIN format=json 
select * 
from t_flow_sterilize_batch a 
join ssd_dev.t_flow_sterilize_batch_instance b 
where a.id = b.sterilize_batch_id

结果为:

{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "3.85"
    },
    "nested_loop": [
      {
        "table": {
          "table_name": "b",
          "access_type": "ALL",
          "rows_examined_per_scan": 8,
          "rows_produced_per_join": 8,
          "filtered": "100.00",
          "cost_info": {
            "read_cost": "0.25",
            "eval_cost": "0.80",
            "prefix_cost": "1.05",
            "data_read_per_join": "8K"
          },
          "used_columns": [
            "id",
            "sterilize_task_id",
            "packet_instance_id",
            "sterilize_batch_id",
            "create_user",
            "create_time",
            "last_user",
            "last_time",
            "delete_flag"
          ],
          "attached_condition": "(`ssd_dev`.`b`.`sterilize_batch_id` is not null)"
        }
      },
      {
        "table": {
          "table_name": "a",
          "access_type": "eq_ref",
          "possible_keys": [
            "PRIMARY"
          ],
          "key": "PRIMARY",
          "used_key_parts": [
            "id"
          ],
          "key_length": "130",
          "ref": [
            "ssd_dev.b.sterilize_batch_id"
          ],
          "rows_examined_per_scan": 1,
          "rows_produced_per_join": 8,
          "filtered": "100.00",
          "cost_info": {
            "read_cost": "2.00",
            "eval_cost": "0.80",
            "prefix_cost": "3.85",
            "data_read_per_join": "13K"
          },
          "used_columns": [
            "id",
            "box_id",
            "batch_no_day",
            "load_count",
            "status",
            "load_user",
            "load_start_time",
            "load_end_time",
            "sterilize_user",
            "sterilize_start_time",
            "sterilize_end_time",
            "sterilize_program_id",
            "batch_no_month",
            "expected_time",
            "create_user",
            "create_time",
            "last_user",
            "last_time",
            "delete_flag",
            "valid_num",
            "valid_unit"
          ]
        }
      }
    ]
  }
}

   转载规则


《Mysql的explain》 锦泉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Spring事务的配置、参数详情及其原理介绍 (Transactional) Spring事务的配置、参数详情及其原理介绍 (Transactional)
消毒供应室系统使用的是SpringBoot+Mybatis+Mysql搭建的框架,因为出bug了,涉及Spring事务管理机制的学习,通过观察服务器日志和整理网上的资料,记录整理一下事务学习的心得。
2020-12-31
下一篇 
MySQL 的Join及底层实现原理 MySQL 的Join及底层实现原理
mysql只支持一种join算法:Nested-Loop Join(嵌套循环连接),但Nested-Loop Join有三种变种
2020-12-30
  目录