您现在的位置是:首页 >学无止境 >Mysql 学习(十 二)查询优化 Explain网站首页学无止境
Mysql 学习(十 二)查询优化 Explain
什么是Explain?
- 一条查询语句经过Mysql查询优化器的各种基于成本和规则的优化后生成一个所谓的执行计划,而Explain 语句可以让我们知道执行计划的语法,从而我们有针对性的提升性能
- 举例子:
EXPLAIN SELECT 1
- 由此我们得到了一些参数,而这些参数我们可以知道我们这个执行计划做了那些优化,由此我们也可以依据这个去优化我们的sql
- 对应参数如下:
- table:
- id:
- select_type:
- partitions:
- type:
- possible_keys:
- key:
- key_len:
- ref:
- rows:
- filtered:
- Extra:
- 接下来我们就来讲解对应参数的含义
table
- 由前几节我们知道,无论查询怎么复杂,我们查询实际上算是对每个表进行单表查询,所以在执行explain 语法,table列上会存放这次查询的表名
- 举个例子:
EXPLAIN SELECT * FROM city
- 上面是单表查询的,如果是连接查询,会怎么展示呢?
- 举个例子:
EXPLAIN SELECT * FROM city LEFT JOIN country on city.country_id = country.id
- 从这里可以看到有两条记录,分别对应city表和country表,通过其他参数我们可以知道,我们访问这两个表分别使用了什么查询方式
id
- 任何查询都会有select ,普通的查询里面可能只有一个select,但是又子查询这种复杂查询就可能会有多个select,但我们知道本质上我们还是把一个拥有多个select语句拆分成多个select语句,所以我们进行优化的时候,需要给每个select定义一个id,这样才好分析
- 情况一:只有一个select ,举个例子:
EXPLAIN SELECT * FROM city LEFT JOIN country on city.country_id = country.id
- 情况二:有多个select,举个例子:
EXPLAIN SELECT * FROM city WHERE country_id = (SELECT id FROM country where country = 'Austria')
- 情况三:查询优化器可能对涉及子查询的查询语句进行重写,从而转换为连接查询。所以如果我们想知道查询优化器对某个包含子查询的语句是否进行了重写,直接查看执行计划就好了,比如:
EXPLAIN SELECT * FROM city WHERE country_id in (SELECT id FROM country )
- 情况四:union 子句,它会将多个查询的结果集合并起来并对结果集中的记录进行去重,会创建一个临时表,所以会产生一个id为null的记录,举个例子:
EXPLAIN SELECT * FROM city UNION SELECT * FROM city
select_type
- 每个select的查询都会有不同的属性,我们之前之所以定义id 主要是为了更好的优化select语句,让我们知道每个select执行了那些操作,而select_type属性就是为了更好研究
- select_type 取值:
-
SIMPLE:查询中不包含UNION或者子查询的查询方案
-
PRIMARY:查询中包含UNION,UNION ALL或者子查询的大查询中,由几个小查询组成,其中最左边的查询就是PRIMARY
- 举个例子:
EXPLAIN SELECT * FROM city UNION SELECT * FROM city
- 举个例子:
-
UNION:对于包含UNION或者UNION ALL的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以外,其余的小查询的select_type值就是UNION
- 举个例子:
EXPLAIN SELECT * FROM city UNION SELECT * FROM city
- 举个例子:
-
UNION RESULT:MySQL选择使用临时表来完成UNION查询的去重工作,针对该临时表的查询的select_type就是UNION RESULT
- 举个例子:
EXPLAIN SELECT * FROM city UNION SELECT * FROM city
- 举个例子:
-
SUBQUERY:如果包含子查询的查询语句不能够转为对应的semi-join的形式,并且该子查询是不相关子查询,并且查询优化器决定采用将该子查询物化的方案来执行该子查询时,该子查询的第一个SELECT关键字代表的那个查询的select_type就是SUBQUERY
- 举个例子:
EXPLAIN SELECT * FROM city WHERE country_id IN (SELECT id FROM country ) OR postal_code = 1010
- 举个例子:
-
DEPENDENT SUBQUERY:如果包含子查询的查询语句不能够转为对应的semi-join的形式,并且该子查询是相关子查询,则该子查询的第一个SELECT关键字代表的那个查询的select_type就是DEPENDENT SUBQUERY
-
DEPENDENT UNION:在包含UNION或者UNION ALL的大查询中,如果各个小查询都依赖于外层查询的话,那除了最左边的那个小查询之外,其余的小查询的select_type的值就是DEPENDENT UNION
-
DERIVED:对于采用物化的方式执行的包含派生表的查询,该派生表对应的子查询的select_type就是DERIVED
-
MATERIALIZED:当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接查询时,该子查询对应的select_type属性就是MATERIALIZED
-
UNCACHEABLE SUBQUERY
-
UNCACHEABLE UNION
-
partitions
- 一般情况下我们的查询语句的执行计划的partitions列的值都是NULL。
type
- 前几节我们知道查询是有访问方式,而在这里type字段就可以展示这个select语句是使用那种访问方式查询数据的
- 类别:
-
system:当表中只有一条记录并且该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory,那么对该表的访问方法就是system
-
const:当我们根据主键或者唯一二级索引列与常数进行等值匹配时,对单表的访问方法就是const
-
eq_ref:在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则对该被驱动表的访问方法就是eq_ref
-
ref: 当通过普通的二级索引列与常量进行等值匹配时来查询某个表,那么对该表的访问方法就可能是ref
-
fulltext:全文索引
-
ref_or_null:当对普通二级索引进行等值匹配查询,该索引列的值也可以是NULL值时,那么对该表的访问方法就可能是ref_or_null
-
index_merge:一般情况下对于某个表的查询只能使用到一个索引,但我们介绍单表访问方法时特意强调了在某些场景下可以使用Intersection、Union、Sort-Union这三种索引合并的方式来执行查询
-
unique_subquery:类似于两表连接中被驱动表的eq_ref访问方法,unique_subquery是针对在一些包含IN子查询的查询语句中,如果查询优化器决定将IN子查询转换为EXISTS子查询,而且子查询可以使用到主键进行等值匹配的话,那么该子查询执行计划的type列的值就是unique_subquery
-
index_subquery:index_subquery与unique_subquery类似,只不过访问子查询中的表时使用的是普通的索引
-
range:如果使用索引获取某些范围区间的记录,那么就可能使用到range访问方法
-
index:当我们可以使用索引覆盖,但需要扫描全部的索引记录时,该表的访问方法就是index
-
ALL:全表扫描
-
possible_keys
- 在EXPLAIN语句输出的执行计划中,possible_keys列表示在某个查询语句中,对某个表执行单表查询时可能用到的索引有哪些
- 比如下面这个例子,可能使用到的索引是idx_key1和idx_key3
key
- 在EXPLAIN语句输出的执行计划中,possible_keys列表示在某个查询语句中,对某个表执行单表查询时实际用到的索引有哪些
- 比如下面这个例子,使用到的索引是idx_key3
key_len
- key_len列表示当优化器决定使用某个索引执行查询时,该索引记录的最大长度,它是由这三个部分构成的:
- 对于使用固定长度类型的索引列来说,它实际占用的存储空间的最大长度就是该固定值,对于指定字符集的变长类型的索引列来说,比如某个索引列的类型是VARCHAR(100),使用的字符集是utf8,那么该列实际占用的最大存储空间就是100 × 3 = 300个字节。
- 如果该索引列可以存储NULL值,则key_len比不可以存储NULL值时多1个字节
- 对于变长字段来说,都会有2个字节的空间来存储该变长列的实际长度。
ref
- 当使用索引列等值匹配的条件去执行查询时,也就是在访问方法是const、eq_ref、ref、ref_or_null、unique_subquery、index_subquery其中之一时,ref列展示的就是与索引列作等值匹配的东东是什么,比如只是一个常数或者是某个列
rows
- 如果查询优化器决定使用全表扫描的方式对某个表执行查询时,执行计划的rows列就代表预计需要扫描的行数,如果使用索引来执行查询时,执行计划的rows列就代表预计扫描的索引记录行数。
filtered
- 之前说连接查询的时候计算成本有扇出这个概念,而执行计划的filtered列就代表查询优化器预测rows列中的记录有多少条满足其余搜索条件,这里存的是百分比,比如下面:
- 从执行计划中可以看出来,查询优化器打算把s1当作驱动表,s2当作被驱动表。我们可以看到驱动表s1表的执行计划的rows列为9688, filtered列为10.00,这意味着驱动表s1的扇出值就是9688 × 10.00% = 968.8,这说明还要对被驱动表执行大约968次查询。