EXPLAIN 语句输出的各个列解释
# EXPLAIN 语句输出的各个列解释
列名 | 描述 |
---|---|
id | 在一个大的查询语句中每个 SELECT 关键字都对应一个唯一的 id |
select_type | SELECT 关键字对应的那个查询的类型 |
table | 表名 |
partitions | 匹配的分区信息 |
type | 针对单表的访问方法 |
possible_keys | 可能用到的索引 |
key | 实际上使用的索引 |
key_len | 实际使用到的索引长度 |
ref | 当使用索引列等值查询时,与索引列进行等值匹配的对象信息 |
rows | 预估的需要读取的记录条数 |
filtered | 某个表经过搜索条件过滤后剩余记录条数的百分比 |
Extra | 一些额外的信息 |
# select_type
每一个 SELECT 关键字代表的小查询都定义了一个称之为 select_type 的属性,意思是我们
只要知道了某个小查询的 select_type
属性,就知道了这个小查询在整个大查询中扮演了一个什么角色
# SIMPLE
查询语句中不包含 UNION 或者子查询的查询都算作是 SIMPLE 类型,连接查询也算是 SIMPLE 类型。
# PRIMARY
对于包含 UNION 、 UNION ALL 或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个查询 的 select_type 值就是 PRIMARY
# UNION
对于包含 UNION 或者 UNION ALL 的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询以 外,其余的小查询的 select_type 值就是 UNION
# UNION RESULT
MySQL 选择使用临时表来完成 UNION 查询的去重工作,针对该临时表的查询的 select_type 就是 UNION RESULT ,例子上边有,就不赘述了。
# SUBQUERY
如果包含子查询的查询语句不能够转为对应的 semi-join 的形式,并且该子查询是不相关子查询,并且查询优化器决定采用将该子查
询物化的方案来执行该子查询时,该子查询的第一个 SELECT 关键字代表的那个查询的 select_type 就是 SUBQUERY
# 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
不常用,就不多唠叨了。
# type
执行计划的一条记录就代表着 MySQL 对某个表的执行查询时的访问方法
system: 当表中只有一条记录并且该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory,那么对该表的访问方法就是 system。
const: 根据主键或者唯一二级索引列与常数进行等值匹配
eq_ref: 在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则对该被驱动表的访问方法就是eq_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 类似,只不过访问子查询中的表时使用的是普通的索引(unique_subquery之前用的是主键索引或者一二级索引)。
range:
如果使用索引获取某些 范围区间
的记录,那么就可能使用到 range 访问方法
index: 当我们可以使用索引覆盖,但需要扫描全部的索引记录时,该表的访问方法就是 index。
ALL: 全表扫描。
# possible_keys和key
possible_keys 列表示在某个查询语句中,对某个表执行单表查询时可能用到的索引有哪些, key 列表示实际用到的索引有哪些。
# key_len
key_len 列表示当优化器决定使用某个索引执行查询时,该索引记录的最大长度,它是由这三个部分构成的:
- 对于使用固定长度类型的索引列来说,它实际占用的存储空间的最大长度就是该固定值,对于指定字符集的变长类型的索引列来说,比如某个索引列的类型是 VARCHAR(100) ,使用的字符集是 utf8 ,那么该列实际占用的最大存储空间就是 100 × 3 = 300 个字节。
- 如果该索引列以存储 NULL 值,则 key_len 比不可以存储 NULL 值时多1个字节。
- 对于变长字段来说,都会有2个字节的空间来存储该变长列的实际长度。
例子
提示:有的同学可能有疑问:你在前边唠叨 InnoDB 行格式的时候不是说,存储变长字段的实际长度不是可能占用1个字节或者2个字节么?为什么现在
不管三七二十一都用了 2 个字节?这里需要强调的一点是,执行计划的生成是在MySQL server 层中的功能,并不是针对具体某个存储引擎的功能,设计
MySQL 的大叔在执行计划中输出key_len 列主要是为了让我们区分某个使用联合索引的查询具体用了几个索引列,而不是为了准确的说明针对某个具体
存储引擎存储变长字段的实际长度占用的空间到底是占用1个字节还是2个字节
# ref
# rows
如果查询优化器决定使用全表扫描的方式对某个表执行查询时,执行计划的 rows 列就代表预计需要扫描的行数,如果使用索引来执行查询时,执行计划的 rows 列就代表预计扫描的索引记录行数
# filtered
之前在分析连接查询的成本时提出过一个 condition filtering 的概念,就是 MySQL 在计算驱动表扇出时采用的一个策略:
- 如果使用的是全表扫描的方式执行的单表查询,那么计算驱动表扇出时需要估计出满足搜索条件的记录到底有多少条。
- 如果使用的是索引执行的单表扫描,那么计算驱动表扇出的时候需要估计出满足除使用到对应索引的搜索条件外的其他搜索条件的记录有多少条。
连接查询中驱动表对应的执行计划记录的filtered 值,比方说下边这个查询:
从执行计划中可以看出来,查询优化器打算把 s1 当作驱动表, s2 当作被驱动表。我们可以看到驱动表 s1 表的执行计划的 rows 列为 9688 , filtered 列为 10.00 ,这意味着驱动表 s1 的扇出值就是 9688 × 10.00% = 968.8 ,这说明还要对被驱动表执行大约 968 次查询。
# Extra
Extra 列是用来说明一些额外信息的,我们可以通过这些额外信息来更准确的理解 MySQL 到底将如何执行给定的查询语句