-
概述
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,那么剩下的就是UNIONd.UNION RESULT(union result查询)
UNION RESULT主要是表示对临时表的查询,如果是对临时表的查询操作,那么就是UNION RESULT,比如union要对生成的临时表去重,那么临时表的select_type就是UNION RESULTe.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, 可以使索引字段少存储一个字节
explain详解
猜你喜欢
转载自blog.csdn.net/weixin_38312719/article/details/105633263
今日推荐
周排行