EXPLAIN 中的列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MySQL> explain select 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: NULL
partitions: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: No tables used
1 row in set, 1 warning (0.00 sec)

id 列

这一列总是包含一个编号,标识 SELECT 所属的行。如果在语句当中没有子查询联合,那么只会有唯一的 SELECT,于是每一行在这个列中都将显示一个 $1$。否则,内层的 SELECT 语句一般会顺序编号,对应于其在原始语句中的位置

MySQL 将 SELECT 查询分为简单和复杂类型,复杂类型可分成三大类:简单子査询、所谓的派生表(在 FROM 子句中的子查询), 以及 UNION 査询。下面是一个简单的子査询:

1
2
3
4
5
6
7
8
MySQL> explain select (select 1 from demo.dept limit 1) from demo.dept;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | PRIMARY | dept | NULL | index | NULL | PRIMARY | 4 | NULL | 1 | 100.00 | Using index |
| 2 | SUBQUERY | dept | NULL | index | NULL | PRIMARY | 4 | NULL | 1 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.01 sec)
1
2
3
4
5
# 派生表查询
MySQL> explain select film.id from (select film.id from demo.film) as der;

# UNION 查询
MySQL> explain select 1 from union all select 1;

select_type 列

显示对应行是简单还是复杂 SELECT(如果是后者,那么是三种复杂类型中的哪一种)。SIMPLE 值意味着査询不包括子查询和 UNION。如果查询有任何复杂的子部分,则最外层部分标记为 PRIMARY,其他部分标记如下。

SUBQUERY

包含在 SELECT 列表中的子査询中的 SELECT(换句话说,不在 FROM 子句中)标记为 SUBQUERY.

DERIVED

用来表示包含在 FROM 子句的子查询中的 SELECT, MySQL 会递归执行并将结果放到一个临时表中。服务器内部称其“派生表”,因为该临时表是从子查询中派生来的。

UNION

在 UNION 中的第二个和随后的 SELECT 被标记为 UNION。第一个 SELECT 被标记就好像它以部分外查询来执行。这就是之前的例子中在 UNION 中的第一个 SELECT 显示为 PRIMARY 的原因。如果 UNION 被 FROM 子句中的子査询包含,那么它的第一个 SELECT 会被标记为 DERIVED 。

UNION RESULT

用来从 UNION 的匿名临时表检索结果的 SELECT 被标记为 UNION RESULT。

table 列

显示了对应行正在访问哪张表,或者表的别名。当 FROM 子句中有子查询或有 UNION 时,table 列会变得复杂许多。

第六章-左侧深度优先树。MySQL 的查询执行计划总是左侧深度优先树。

type 列

效率 type 列中项 解释
$\rightarrow$ const/system 常量,表只有一行(用于 MyISAM、Memory)。
$\uparrow$ eq_ref 搜索时使用 PK 或 UNIQUE KEY,常用于多表联查。
$\uparrow$ ref 根据索引查找一个或多个值。
$\uparrow$ index_merge 合并索引,使用多个单列索引查找。
$\uparrow$ range 按索引进行范围查找。
$\uparrow$ index 按索引全表扫描。
$\uparrow$ all 所谓的全表扫描。

访问类型,就是 MySQL 决定如何査找表中的行。下面是最重要的访问方法,依次从最差到最优。

ALL

所谓的全表扫描,通常意味着 MySQL 必须扫描整张表,从头到尾,去找到需要的行。

例外:在查询里使用了 LIMIT,或者在 Extra 列中显示“Using distinct/not exists“。

index

跟全表扫描一样,只是 MySQL 扫描表时按索引次序进行而不是行次序。它的主要优点是避免了排序;最大的缺点是要承担按索引次序读取整个表的开销。这通常意味着若是按随机次序访问行,开销将会非常大。

如果在 Extra 列中看到 “Using index“,说明 MySQL 正在使用覆盖索引,它只扫描索引的数据,而不是按索引次序的每一行。它比按索引次序全表扫描的开销要少很多。

range

范围扫描就是一个有限制的索引扫描,它开始于索引里的某一点,返回匹配这个值域的行。比全索引扫描好一些,因为不用遍历全部索引。显而易见的范围扫描是带有 BETWEEN 或在 WHERE 子句里带有$>$ 的查询。

当 MySQL 使用素引去查找一系列值时,例如 IN() 和 0R 列表,也会显示为范围扫描。然而,这两者其实是相当不同的访向类型,在性能上有重要的差异。

此类扫描的开销跟索引类型相同。

ref

一种索引访问(又称索引查找),它返回所有匹配某个单个值的行。然而,它可能会找到多个符合条件的行,因此,它是查找和扫描的混合体。此类索引访问只有当使用非唯一性索引或者唯一性索引的非唯一性前缀时才会发生。把它叫做 ref 是因为索引要跟某个参考值相比较。这个参考值或者是一个常数,或者是来自多表査询前一个表里的结果值。

​ ref or null 是 ref 之上的一个变体,它意味着 MySQL 必须在初次査找的结果里进行第二次査找以找出 NULL 条目。

eq_ref

使用这种索引査找,MySQL 知道最多只返回一条符合条件的记录。这种访问方法可以在 MySQL 使用主键或者唯一性索引査找时看到,它会将它们与某个参考值做比较。

const, system

当 MySQL 能对查询的某部分进行优化并将其转换成一个常量时,它就会使用这些访问类型。

NULL

意味着 MySQL 能在优化阶段分解查询语句,在执行阶段甚至用不着再访问表或者索引。例如,从一个索引列里选取最小值。

possible_keys 列

显示了查询可以使用哪些索引。

key 列

显示了 MySQL 决定采用哪个索引来优化对该表的访问。如果该索引没有出现在 possible_keys 列中,那么 MySQL 选用它是出于另外的原因。例如,它可能选择了一个覆盖素引,即使没有 WHERE 子句。

换言之,possible_keys 揭示了哪一个索引能有助于高效地行査找,而 key 显示的是优化采用哪一个索引可以最小化查询成本。

key_len 列

显示了 MySQL 在索引里使用的字节数。

ref 列

显示了之前的表在 key 列记录的索引中查找值所用的列或常量。

rows 列

MySQL 估计为了找到所需的行需要读取的行数。

filtered 列

针对表里某个符合条件(WHERE 子句或者连接条件)的记录数的百分比所做的一个悲观估算。如果跟 rows 列相乘,就能看到 MySQL估算它将和查询计划里前一个表关联的行数。

Extra 列

Extra 列中项(部分) 解释
“Using index” MySQL 将使用覆盖索引,以避免访问表。不要把覆盖索引和 index 访问类型弄混。
“Using where” MySQL 服务器将在存储引擎检索行后再进行过滤。在 WHERE 条件里涉及索引列时,就能被存储引擎检验,不是所有带 WHERE 子句的查询都会显示 “Using where”
“Using temporary” MySQL 在对查询结果排序时会使用一个临时表。
“Using filesort” MySQL 会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。
“Range checked for each record (index map: N)” 没有好用的索引,新的索引将在联接的每一行上重新估算。N 是显示在 possible_keys 列中索引的位图,并且是冗余的。

参考

  • 《高性能MySQL》

评论