explain详解

  • 概述
    explain是用来查看sql的执行计划的,每当我们想要看看sql执行计划,或者由于性能原因更是需要分析执行计划· 。一般来说sql执行计划都是这样的,我们需要明白每个字段的含义。
    在这里插入图片描述

  • table字段
    table字段表示sql执行计划的每一步都涉及的表,具体是对哪一张表的操作
    举例:
    explain select * from s1
    在这里插入图片描述
    explain select * from s1 inner join s2
    在这里插入图片描述

  • id字段
    凡是explain的sql语句中有一个select, 那么就会分配一个id
    举例:
    explain SELECT * FROM s1 WHERE key1 = ‘sHNz1Mhr’;
    在这里插入图片描述
    explain SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key1;(同一个select,它的id相同)
    (注意: 连接查询虽然涉及两张表,但是还是一个select,还是分配一个相同id,出现在前面的是驱动表,s2是驱动表,s1是被驱动表)
    在这里插入图片描述
    EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = ‘sHNz1Mhr’;
    (说明: 这里包含子查询,所以有两个select,那么也就分配了两个id. 特殊情况下,会对子查询重写,这样会把两个查询转换为连接,就只会分配一个id,转换为上面的情况)
    在这里插入图片描述
    explain SELECT * FROM s1 UNION SELECT * FROM s2
    (说明:这里union操作,为了合并两个表查询结果分配了两个id 1和 2,但是最后一行生成了一个null的,表名为<union1,2> 其实这个是个临时表,也是这个语句要操作的一部分,那就是对合并的表去重,union all就没有最后一行,因为不需要去重而操作临时表)
    在这里插入图片描述

  • select_type字段
    slect_type表示查询的类型

    举例:
    a.SIMPLE(简单查询)

    explain select * from s1;在这里插入图片描述
    EXPLAIN SELECT * FROM s1 INNER JOIN s2;
    (说明: 连接也算SIMPLE)
    在这里插入图片描述
    b.PRIMARY(主查询)
    (一些由多个select 拼接出来的查询都要分主次,比如 union,union all ,子查询,一般最左边的是主查询)

    explain SELECT * FROM s1 UNION SELECT * FROM s2;
    (说明:union合并了两个子查询,对s1的查询是主查询)
    在这里插入图片描述
    explain SELECT * FROM s1 UNION all SELECT * FROM s2;
    (说明:union all 不去重合并两个子查询,对s1查询是主查询)
    在这里插入图片描述
    EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = ‘sHNz1Mhr’;
    (说明: 这是个很明显有子查询的sql, 对S2是子查询,对s1是主查询)
    在这里插入图片描述
    c.UNION(union 查询)
    UNION主要是针对union,union all两种查询来说的,如果最左边的是PRIMARY,那么剩下的就是UNION

    d.UNION RESULT(union result查询)
    UNION RESULT主要是表示对临时表的查询,如果是对临时表的查询操作,那么就是UNION RESULT,比如union要对生成的临时表去重,那么临时表的select_type就是UNION RESULT

    e.SUBQUERY(子查询)

    EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = ‘sHNz1Mhr’;
    (说明: 这是个很明显有子查询的sql, 对S2是子查询,对s1(最左边)是主查询)
    在这里插入图片描述

  • type字段
    type字段表示查询访问方法

    1 const(主要对于唯一能确定一条记录的索引,比如主键和唯一索引,唯一索引的等值检索)

    explain select * from s1 where key2=1340434282;
    在这里插入图片描述
    2 .ref(二级普通索引的等值检索)
    explain select * from s1 where key1=“aaa”;
    在这里插入图片描述
    3.range(索引的范围检索,包括唯一索引,主键,普通索引. 特殊情况会转换为const,ref,比如范围就一个值 id in (10) 这种)
    explain select * from s1 where id>10;
    在这里插入图片描述
    4.index(当全表扫描,但是直接使用覆盖索引,不需要回表的时候就是index, 毕竟扫描主键索引,扫描二级索引成本要低很多)
    EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3= ‘a’;
    在这里插入图片描述
    EXPLAIN SELECT * FROM s1 WHERE key_part3 = ‘a’;
    在这里插入图片描述

  • possible_keys和key字段
    possible_keys表示可能用到的索引, key表示最终使用的索引,优化器会挑选成本最低的去执行

  • key_len字段
    key_len看到这个字段的名称应该就知道和key有关系了,它指的含义就是该索引记录的最大长度的字节数

    explain select * from s1 where id=10;
    (说明:使用的索引是id 是int类型的,长度为4个字节)
    在这里插入图片描述
    explain select * from s1 where key1=“aaa”;
    (说明: 使用的索引是key1, 他的类型是varchar(100), 因为是utf8编码,一个字符三个字节也就是300;因为是可变,那么需要2个字节记录长度;key1可以为null,需要再多1个字节;所以是303)
    在这里插入图片描述
    explain select * from s1 where key2=1786216152;
    (说明: 使用key2作为索引,key2的类型为int(11), 底层是使用4个字节存储的,但是key2是允许为null的,所以需要多1个字节;所以是5)
    在这里插入图片描述

  • rows字段
    rows指的是每一行执行计划的操作的扫描行数,因为走了索引只是过滤了不满足索引字段的记录,and后面可能还有条件过滤,所以需要扫描完后,继续拿满足索引的主键进行回表继续过滤。

    explain select * from s1 where id=10;
    (说明:使用的索引是主键索引,扫描一行就可以了)
    在这里插入图片描述
    explain select * from s1;
    (说明: 全表扫描,将近10000行,扫描的是聚簇索引B+树)
    在这里插入图片描述

  • Extra字段
    extra字段可以更加准确描述你的sql是怎么执行的

    1.No tables used
    (说明:查询的时候没有选择表时)
    explain select 1;
    在这里插入图片描述

    2.Impossible WHERE
    (说明:where 子句条件永远为false)
    explain select * from s1 where id is null;
    在这里插入图片描述
    3.No matching min/max row
    (说明:当我们使用聚合函数Max/Min,但是where子句并没有匹配到任何记录,那么不可能存在Max/Min)
    explain select Max(id) from s1 where key1=“aaaaaaaaa”;
    在这里插入图片描述
    4.using index
    (说明:当我们使用二级索引时,直接使用到覆盖索引不需要回表时)
    explain select key1,id from s1 where key1>“w”;(普通索引只包含索引字段和主键字段值)
    在这里插入图片描述
    5.Using index condition
    (说明:对于二级索引回表的情况,能在二级索引过滤掉的都过滤掉,减少回表次数)
    explain select * from s1 WHERE key1>“W” and key1 like “%addsds”;
    在这里插入图片描述
    6.Using where
    (说明:当where后面条件查询时,但是走不了索引的情况)
    explain select * from s1 where common_field>“aa”;
    在这里插入图片描述
    explain select * from s1 where key1>“y” and common_field>“a”;
    在这里插入图片描述
    7.Using filesort
    (说明:主要是针对排序的,如果查询的记录需要排序,如果没有对应的索引,那么就需要在内存或者磁盘进行排序,这个时候就是Using filesort)
    explain select * from s1 order by common_field limit 10;
    在这里插入图片描述
    explain select * from s1 where key1>“w” order by key2;
    在这里插入图片描述
    8.Using temporary
    (说明: 当我们用到了临时表(排序,去重,分组)的时候,会触发这个类型的extra)
    explain select distinct common_field from s1 where common_field>“w”;
    在这里插入图片描述
    explain SELECT * FROM s1 UNION SELECT * FROM s2;
    在这里插入图片描述
    EXPLAIN SELECT common_field, COUNT(*) AS amount FROM s1 GROUP BY common_field;
    在这里插入图片描述

  • json格式的执行计划(几乎包含每一步的执行成本花费)
    explain format=json select * from s1 where id>“9000”

    {
          
          
      "query_block": {
          
          
        "select_id": 1,
        "cost_info": {
          
          
          "query_cost": "404.08"//整个查询成本预计
        },
        "table": {
          
          
          "table_name": "s1",
          "access_type": "range",
          "possible_keys": [
            "PRIMARY"
          ],
          "key": "PRIMARY",
          "used_key_parts": [
            "id"
          ],
          "key_length": "4"//索引长度
          "rows_examined_per_scan": 1003,//扫描条数
          "rows_produced_per_join": 1003,//扇出
          "filtered": "100.00",
          "cost_info": {
          
          
            "read_cost": "203.48", //rows*(1-filter)
            "eval_cost": "200.60",//rows × filter
            "prefix_cost": "404.08",//read_cost + eval_cost
            "data_read_per_join": "1M"//查询的数据量
          },
          "used_columns": [//涉及到的列
            "id",
            "key1",
            "key2",
            "key3",
            "key_part1",
            "key_part2",
            "key_part3",
            "common_field"
          ],
          "attached_condition": "(`mytest`.`s1`.`id` > '9000')"
        }
      }
    }
    
  • 总结
    1.指定了索引不一定就会走索引,比如范围查询,如果查询的数据条数占一定比例了,那么就选择直接扫描聚簇索引了,这样可以减少回表成本(走索引只是过滤了不满足索引的记录,还有非索引字段需要过滤(and a=1 …),在二级索引是不存在这些数据的,是要回表进行过滤)。
    2 .索引尽量使用not null, 可以使索引字段少存储一个字节

猜你喜欢

转载自blog.csdn.net/weixin_38312719/article/details/105633263