mysql explain

1. 关于mysql explain

  • explain是mysql提供的用于查看对于一条sql语句mysql的执行计划,比如查询次数,查询是否使用索引,预期扫描行数等等,可以使用该工具进行mysql优化。

  • 使用方法:在select语句前加explain。

以下为一个简单的使用索引查询的解释计划:

mysql> EXPLAIN SELECT * FROM employees t WHERE t.`emp_no`='10001';
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t     | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

2. explain结果各个数据项详解

2.1. 数据行

explain结果可能会出现多行有序数据,表示执行所需要的查询次数及次序。比如在涉及子查询、union等操作时。

示例1:

mysql> EXPLAIN SELECT t.*,e.* FROM employees t LEFT JOIN dept_emp e ON t.`emp_no`=e.`emp_no`;
+----+-------------+-------+------------+------+----------------+--------+---------+--------------------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys  | key    | key_len | ref                | rows   | filtered | Extra       |
+----+-------------+-------+------------+------+----------------+--------+---------+--------------------+--------+----------+-------------+
|  1 | SIMPLE      | t     | NULL       | ALL  | NULL           | NULL   | NULL    | NULL               | 299343 |   100.00 | NULL        |
|  1 | SIMPLE      | e     | NULL       | ref  | PRIMARY,emp_no | emp_no | 4       | employees.t.emp_no |      1 |   100.00 | Using index |
+----+-------------+-------+------------+------+----------------+--------+---------+--------------------+--------+----------+-------------+

示例2
当使用union时会有额外的行信息:

EXPLAIN SELECT 1 UNION SELECT 1;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
|  id  | select_type  | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
+------+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
|  1   | PRIMARY      | NULL       | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | No tables used  |
|  2   | UNION        | NULL       | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | No tables used  |
| NULL | UNION RESULT | <union1,2> | NULL       | ALL  | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+

使用union all不同于union,union不产生临时表

mysql> mysql> EXPLAIN SELECT 1 UNION ALL SELECT 1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra          |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
|  1 | PRIMARY     | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | No tables used |
|  2 | UNION       | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | No tables used |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+

2.2. 数据列

2.2.1. id

唯一标识sql中的每一个(子)查询,内层的select语句一般会顺序编号,对应于其在原始语句中的位置。

2.2.2. select_type

该列显示了对应行是简单还是复杂select。simple代表只有简单查询,不含有子查询和union。如果查询含有任何复杂查询则最外层查询为primary,其他部分标记为如下:

  • SUBQUERY
    包含在select的属性列表中select标记为SUBQUERY.SUBQUERY有很多派生类型

示例:

EXPLAIN SELECT t.`emp_no`, (SELECT it.`first_name` FROM employees it WHERE t.`emp_no`=it.`emp_no` AND it.`emp_no`='10001' ) FROM employees t;
+----+--------------------+-------+------------+-------+---------------+------------------------+---------+-------+--------+----------+-------------+
| id | select_type        | table | partitions | type  | possible_keys | key                    | key_len | ref   | rows   | filtered | Extra       |
+----+--------------------+-------+------------+-------+---------------+------------------------+---------+-------+--------+----------+-------------+
|  1 | PRIMARY            | t     | NULL       | index | NULL          | ind_employees_birthday | 3       | NULL  | 299343 |   100.00 | Using index |
|  2 | DEPENDENT SUBQUERY | it    | NULL       | const | PRIMARY       | PRIMARY                | 4       | const |      1 |   100.00 | NULL        |
+----+--------------------+-------+------------+-------+---------------+------------------------+---------+-------+--------+----------+-------------+
  • DERIVED
    包含在From子句中的子查询中的select,mysql会递归执行并将结果放到一个临时表中,服务器内部称其为派生表。
    示例:

  • UNION
    在union中的第二个和随后的select被标记为union

  • UNION RESULT
    用来从union的匿名临时表检索结果的select被标记我iunion result

2.2.3. table

标识当前行查询的表的名称或表的别名。特别的表明:derivedN,表示为内部查询匿名临时表,N表示向前引用计数,N指向EXPLAIN输出中后面的一行。

2.2.4. partitions

标识当前查询的表是否是分区表。

2.2.5. type

该列显示访问类型,从最差到最优依次为:

  • ALL
    全表扫描。例外:当使用了limit或者Extra列中显示Using distinct/not exists时,非全表扫描。
  • index
    跟全表扫描一样,只是mysql扫描表时按索引次序进行而不是行。
  • range
    有限制的索引扫描,优于全索引扫描。关键信息:索引+基于索引的范围查找
  • ref
    索引访问或者索引查找。返回所有匹配某个单个值的行,可能找到多个符合条件的行。关键信息:非唯一索引/唯一索引的非唯一性前缀 + 匹配单个值
    示例:

    -- birth_date建立了普通索引
    mysql> EXPLAIN SELECT * FROM employees t WHERE t.birth_date='1990-01-01';
    +----+-------------+-------+------------+------+------------------------+------------------------+---------+-------+------+----------+-------+
    | id | select_type | table | partitions | type | possible_keys          | key                    | key_len | ref   | rows | filtered | Extra |
    +----+-------------+-------+------------+------+------------------------+------------------------+---------+-------+------+----------+-------+
    |  1 | SIMPLE      | t     | NULL       | ref  | ind_employees_birthday | ind_employees_birthday | 3       | const |    1 |   100.00 | NULL  |
    +----+-------------+-------+------------+------+------------------------+------------------------+---------+-------+------+----------+-------+
    

    ref_or_null, ref的变体,表示mysql必须在初次查找的结果里进行第二次查找以找出NULL条目。

  • eq_ref
    mysql知道最多返回一条符合条件的记录。关键信息:主键/唯一索引+匹配单个值.
    示例:

    -- emp_no为主键
    mysql> EXPLAIN SELECT * FROM employees t WHERE t.emp_no IN (SELECT emp_no FROM dept_emp WHERE dept_no='d005');
    +----+-------------+----------+------------+--------+------------------------+---------+---------+---------------------------+--------+----------+-------------+
    | id | select_type | table    | partitions | type   | possible_keys          | key     | key_len | ref                       | rows   | filtered | Extra       |
    +----+-------------+----------+------------+--------+------------------------+---------+---------+---------------------------+--------+----------+-------------+
    |  1 | SIMPLE      | dept_emp | NULL       | ref    | PRIMARY,emp_no,dept_no | dept_no | 12      | const                     | 148054 |   100.00 | Using index |
    |  1 | SIMPLE      | t        | NULL       | eq_ref | PRIMARY                | PRIMARY | 4       | employees.dept_emp.emp_no |      1 |   100.00 | NULL        |
    +----+-------------+----------+------------+--------+------------------------+---------+---------+---------------------------+--------+----------+-------------+
    
  • const, system
    当mysql能对查询到某部分进行优化并将其转换成一个常量,将会使用该访问类型。

示例

-- emp_no为主键
mysql> EXPLAIN SELECT * FROM employees t WHERE t.emp_no='10001';
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t     | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
  • NULL
    这种方式表示mysql能在优化阶段分解查询语句,在执行阶段甚至用不着再访问或者索引。
-- emp_no为主键
EXPLAIN SELECT max(emp_no) FROM employees;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                        |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
|  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+

2.2.6. possible_keys

可能使用到对索引,在优化早期创建,有些在后续优化过程可能用不到。

2.2.7. key

mysql实际使用对索引,该值不一定是在possible_keys中。

示例:

-- 覆盖索引
mysql> EXPLAIN SELECT emp_no FROM employees;
+----+-------------+-----------+------------+-------+---------------+------------------------+---------+------+--------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key                    | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+------------------------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | employees | NULL       | index | NULL          | ind_employees_birthday | 3       | NULL | 299343 |   100.00 | Using index |
+----+-------------+-----------+------------+-------+---------------+------------------------+---------+------+--------+----------+-------------+

2.2.8. key_len

mysql在索引里使用对字节数。

2.2.9. ref

2.2.10. rows

mysql估计为了找到所需的行而要读取对行数,估算不精确。

2.2.11. filtered

显示的是针对表里符合某个条件对记录数的悲观比例估算。

2.2.12. Extra

在其他列不适合展示的信息。常见信息如下:

  • Using index
    此值表示mysql将使用覆盖索引
  • Using where
    此值表示mysql将在存储引擎检索行后再进行过滤(回表)

  • Using temporary
    此值表示对查询结果排序时会使用一个临时表。

  • Using filesort
    此值表示mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。

  • Range checked for each record(index map:N)
    此值表示没有好用的索引,新的索引将在链接的每一行上重新估算。N是显示在possible_keys列中索引的位图,并且是冗余的。

👉 REF:高性能mysql