第15章 查询优化的百科全书——Explain输出详解

mysql提供了explain语句,让我们可以查看某个语句的具体执行计划。

explain语句输出中各个列的作用 #

  • id。每个select关键字都对应一个唯一的id(语句可能被重写)。
  • select_type select关键字对应的查询的类型。
  • table 表名。
  • partitions 匹配的分区。
  • type 针对单表的访问方法。
  • possible_keys 可能使用的索引。
  • key 实际使用的索引。
  • key_len 实际使用的索引的长度。
  • ref 当使用索引列等值查询时,与索引列进行等值匹配的对象信息。
  • rows 预估需要读取的记录数。
  • filtered:针对预估的需要读取的记录,经过搜索条件过滤后剩余记录条数的百分比。
  • extra 额外信息。

执行计划输出各列详解 #

table #

输出中的每条记录都对应着某个单表访问方法,该条记录的table列代表该表的表名。

id #

查询语句中每出现一个select关键字,mysql就会为它分配一个唯一的id。 在连接查询的执行计划中,每个表都会对应一条记录,这些记录的id值是相同的;出现在前面的表示驱动表,出现在后面的表表示被驱动表。

对于包含子查询的关键字来说,可能涉及多个select关键字。每个select关键字都会对应一个唯一的id值。

查询优化器可能对涉及子查询的语句进行重写,从而转换为连接查询。

id值为null表名这个临时表是为了合并两个查询的结果集而创建的。

union会把多个查询的结果集合并起来并对结果集中的记录进行去重。

union all不需要对结果集进行去重。

select_type #

mysql为每一个selct关键字代表的小查询都定义了一个名为select_type的属性。只要知道了某个小查询的select_type属性,也就知道了这个小查询在整个大查询中扮演的角色。

此列的值可能是以下几种(列举主要的几种):

  • simple 简单查询,语句中不包含union或者子查询的查询都算simple类型。连接查询的select_type也是simple。
  • 对于包含union,union all或者子查询的大查询来说,它是由几个小查询组成的;其中最左边的那个查询的select_type的值就是primary。
  • union 对于包含union或union的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以为,其余小查询的select_type的值就是union。
  • union result mysql使用临时表来完成union查询的去重工作,针对该临时表的查询的select_type就是union result。
  • subquery 如果包含子查询的查询语句不能转为对应的半连接形式,并且该子查询是不想管子查询,而且查询优化器决定采用将该子查询物化的方案来执行该子查询时。该子查询的第一个select关键字代表的那个查询的select_type就是subquery。
  • materialized 当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接,该子查询对应的select_type属性就是materialized。

type #

代表着mysql对某张表执行查询时的访问方法。主要值有以下几种:

  • system 表中只有一条记录并且该表使用的存储引擎的统计数据是精确的。
  • const 根据主键或者唯一二级索引列与常数进行等值匹配。
  • eq_ref 执行连接查询,被驱动表时通过主键或者不允许存储null的唯一二级索引等值匹配的方式进行访问的。
  • ref 通过普通二级索引列与常量进行等值匹配的方式来查询。
  • fulltext 全文索引。
  • ref_or_null 对普通二级索引列进行了等值匹配且该索引列的值夜可能是null,对该表的访问方法就可能是null。
  • index_merge 索引合并。
  • range 如果使用索引获取某些单点扫描区间的记录,那么就可能用到range访问方法。
  • index 可以使用覆盖索引,但需要扫描全部的索引记录。 此外对于innodb存储引擎来说,当我们需要执行全表扫描,并且需要对主键进行排序时,此时type列的值夜是index。
  • all 全表扫描。

possible_keys 和key #

possible_keys表示在某个查询中对某张表执行单表查询时可能用到的索引有哪些,key列表示实际使用功能的索引有哪些。 possible_keys中的值并不是越多越好,可以使用的索引越多,查询优化器在计算成本时花费的时间就越长。

key_len #

表示mysql在使用索引时,索引的长度是多少。 计算方法: - 该列的实际数据最多占用的存储空间长度。 - 如果该列可以存储null,则再加1字节。 - 对于变长类型来说,再加2字节。

**执行计划的生成是server层功能。**

ref #

ref列展示的是与索引列进行等值匹配的是什么,比如只是一个常数或者某个列。

rows #

如果决定使用全表扫描,rows就代表该表的估计行数。

如果使用索引来执行查询,rows就代表语句扫描的索引技术行数。

filter #

查询优化器预测的rows中满足条件的记录的比例。

extra #

  • using index 使用了覆盖索引。
  • using index condition 索引条件下推。有些搜索条件虽然出现了索引列,但却不能充当边界条件来形成扫描区间,也就是不能用来较少扫描的记录数,将会提示该额外信息。 mysql会把扫描条件放在引擎层进行过滤,从而避免了回表。
  • using where 某个搜索条件需要再server层进行判断。
  • using join buffer 连接查询,被驱动表不能有效地利用索引加快访问速度时,mysql一般会为其分配一块名为连接缓冲区的内存来加快查询。
  • using filesort 排序无法使用到索引,只能在内存或磁盘中进行排序。
  • using tempporary 临时表。mysql可能会借助临时表来完成一些功能,比如去重、排序等。 mysql会在包含group by子句的查询中默认添加order by子句。

json格式的执行计划 #

可以在explain和查询语句之间加上format=json来获取json格式的执行计划,查看某个执行计划的查询成本。 其中部分字段:

cost_info:

查询成本信息。字段列表如下:
- read_cost:读取成本。I/O成本+CPU成本。
- eval_cost:检测过滤记录的成本。
- prefix_cost:单独查询x表的成本。
- data_read_per_join:单词查询需要读取的数据量。

注意:在连接查询中,被驱动表可能被读取多次。