高性能MySQL07-EXPLAIN详解

一、简介
explain命令用于分析单条SQL语句,是查看优化器如何决定执行查询的主要方法。

要使用EXPLAIN,只需在查询中的SELECT关键字之前增加EXPLAIN这个词。MySQL会在查询上设置一个标记。当执行查询时,这个标记会使其返回关于在执行计划中每一步的信息,而不是执行它。它会返回一行或多行信息,显示出执行计划中的每一部分和执行的次序。

如:

mysql> explain select id from a where id =3\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: ref
possible_keys: id
          key: id
      key_len: 5
          ref: const
         rows: 1
        Extra: Using where; Using index
1 row in set (0.00 sec)

注意:认为增加EXPLAIN时MySQL不会执行查询,这是一个常见的错误。事实上,如果查询在FROM子句中包括子查询,那么MySQL实际上会执行子查询,将其结果放在一个临时表中,然后完成外层查询优化。
这意味着如果语句包含开销比较大的子查询或使用临时表算法的视图,实际上会给服务器带来大量工作。

二、EXPLAIN的列

1、id

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

MySQL将SELECT查询分为简单和复杂类型,复杂类型可以分为三大类:简单子查询、所谓的派生表(在FROM子句的子查询)、UNION查询。

例(我们只看返回多少行数据就行,其它列先不用理):

# 例1:简单子查询
mysql> explain select (select 1 from a limit 1) from b;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | PRIMARY     | b     | index | NULL          | age     | 2       | NULL |    4 | Using index |
|  2 | SUBQUERY    | a     | index | NULL          | PRIMARY | 4       | NULL |    4 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
2 rows in set (0.00 sec)

# 例2:FROM子句中的子查询
mysql> explain select name from (select name from a) as aa;
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
|  1 | PRIMARY     | <derived2> | ALL  | NULL          | NULL | NULL    | NULL |    4 |       |
|  2 | DERIVED     | a          | ALL  | NULL          | NULL | NULL    | NULL |    4 |       |
+----+-------------+------------+------+---------------+------+---------+------+------+-------+
2 rows in set (0.00 sec)

# 例3:UNION查询
mysql> explain select id from a union select id from b;
+----+--------------+------------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type  | table      | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+--------------+------------+-------+---------------+---------+---------+------+------+-------------+
|  1 | PRIMARY      | a          | index | NULL          | PRIMARY | 4       | NULL |    4 | Using index |
|  2 | UNION        | b          | index | NULL          | PRIMARY | 4       | NULL |    4 | Using index |
| NULL | UNION RESULT | <union1,2> | ALL   | NULL          | NULL    | NULL    | NULL | NULL |             |
+----+--------------+------------+-------+---------------+---------+---------+------+------+-------------+
3 rows in set (0.00 sec)

注意UNION结果输出中的额外行。UNION结果总是放在一个匿名的临时表中,之后MySQL将结果取到临时表外。临时表并不在原SQL中出现,因此它的id列是NULL。

2、select_type

显示对应和行是简单还是复杂SELECT(如果是后者,那么一三种复杂类型中的哪一种)。

SIMPLE值意味着查询不包括子查询和UNION。如果查询中有任何复杂的子部分,则最外层标记为PRIMARY,其它标记如下:

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

2)DERIVED
包含在FROM子句的子查询中的SELECT,MySQL会递归执行并将结果放到一个临时表(服务器内部称其“派生表”)。

3)UNION
UNION中的第二个和随后的SELECT被标记为UNION。

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

3、table

这一列显示了对应正在访问哪个表。通常情况下,它相当明了:它就是那个表,或者是该表的别名。

4、type

MySql用户手册上说这一列显示了“关联关类型”,但我们认为更准确的说法是访问类型——换言之就是MySQl决定如何查找表中的行。下面是最重要的访问方法,依次从最差到最优。

  • ALL
    这就是我们所说的全表扫描。(当然也有例外,例如在查询里使用了LIMIT,或者在Extra列中显示“Using distinct/not exists”)
  • index
    这个跟全表扫描一样,只是MySQL扫描表时按索引次序进行而不是行。它的主要优点是避免了排序;最大缺点是要承担按索引次序读取整个表的开销。如果在Extra列中看到“Using index”,说明MySQL正在使用覆盖索引,它只扫描索引的数据。而不是按索引次序的每一行。
  • range
    范围扫描就是一个有限制的索引扫描,它开始于索引里的某一点,返回匹配这个值域的行。这比全索引扫描好一些,因为它不用遍历全部索引。显而易见的范围扫描是带有BETWEEN或在WHERE子句里带有>的查询。
  • ref
    这是一种索引访问(有时也叫做索引查找),它返回所有匹配是某个单值的行。然而它可能会找到多个符合条件的行,因此,它是查找和扫描的混合体。
  • eq_ref
    使用这种索引查找,MySQL知道最多只返回一条符合条件的记录。这种访问方法可以在MySQL使用主键或者唯一性索引查找时看到。
  • const, system
    当MySQL能对查询的某部分进行优化并将其转化成一个常量时,它就会使用这些访问类型。
  • NULL
    这种访问方式意味着MySQL能在优化阶段分解查询语句,在执行阶段甚至用不着再访问表或者索引。例如,从一个索引列里选取最小值可以通过单独查找索引来完成,不需要在执行时访问表。

5、possible_keys

这一列显示了查询可以使用哪些索引,这是基于查询访问的列和使用的比较操作符来判断的。这个列表是在优化过程的早期创建的,因此有些罗列出来的索引可能对于后续优化过程是没用的。

6、key

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

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

7、key_len

该列显示了MySQL在索引里使用的字节数。如果MySQL正在使用的只是索引里的某些列,那么就可以用这个值来算出具体是哪些列。

8、ref

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

9、 rows

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

10、filtered

这一列是在MySQL 5.1里新加进去的,在使用EXPLAIN EXTENDED时出现。它显示的是针对表里符合某个条件(WHERE子句或联接条件)的记录数的百分比所做的一个悲观估算。如果你把row列和这个百分比相乘,就能看到MySQL估算它将和查询计划里前一个表关联的行数。

11、Extra

这一列包含的是不适合在其它列显示的额外信息。常见的最重要的值如下:

  • Using index
    此值表示MySQL将使用覆盖索引,以避免访问表。
  • 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》 [美]Baron Scbwartz, Peter Zaitsev, Vadim Tkacbenko 著

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章