Mysql explain 命令

前言

MySQL 提供了一个 EXPLAIN 命令, 它可以对 SELECT 语句进行分析, 并输出 SELECT 执行的详细信息。通过该命令看出 mysql 是如何执行该条 sql 的,可以通过分析索引,表结构等方面来优化你的慢查询语句。

用法

EXPLAIN 命令用法十分简单, 在 SELECT 语句前加上 Explain 就可以了, explain + sql 语句, 例如:

EXPLAIN SELECT * FROM blog WHERE blog_id > 10;

执行结果如下

mysql> EXPLAIN SELECT * FROM blog WHERE blog_id > 10;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | blog  | NULL       | range | PRIMARY       | PRIMARY | 8       | NULL |    8 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

执行结果有如下的字段,分别是id、select_type、table、partitions、type、possible_keys、key、key_len、ref、rows、filtered、Extra等等,下面对这些字段出现的可能进行解释:

id

select 查询的标识符。如果在语句中没子查询或关联查询,只有唯一的select,每行都将显示1。否则,内层的select语句一般会顺序编号,对应于其在原始语句中的位置。

  • id相同时,执行顺序由上至下
  • 如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
  • id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

select_type

表示了查询的类型。主要是用于区别普通查询、联合查询、子查询等的复杂查询。

类型 说明
simple 简单子查询,不包含子查询和union
primary 包含union或者子查询,最外层的部分标记为primary
subquery 一般子查询中的子查询被标记为subquery,也就是位于select列表中的查询
dependent subquery 相关子查询
derived 派生表——该临时表是从子查询派生出来的,位于form中的子查询
union 位于union中第二个及其以后的子查询被标记为union,第一个就被标记为primary,如果是union位于from中则标记为derived
union result 用来从匿名临时表里检索结果的select被标记为union
dependent union 顾名思义,首先需要满足UNION的条件,及UNION中第二个以及后面的SELECT语句,同时该语句依赖外部的查询
uncacheable subquery 一个子查询的结果不能被缓存,必须重新评估外链接的第一行

table

表示查询涉及的表或衍生表。

mysql> explain select * from (select * from ( select * from t1 where id=2602) a) b;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table      | type   | possible_keys     | key     | key_len | ref  | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
|  1 | PRIMARY     | <derived2> | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  2 | DERIVED     | <derived3> | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  3 | DERIVED     | t1         | const  | PRIMARY,idx_t1_id | PRIMARY | 4       |      |    1 |       |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+

当from中有子查询的时候,表名是derivedN的形式,N指向子查询,也就是explain结果中的下一列
当有union result的时候,表名是union 1,2等的形式,1,2表示参与union的query id
注意:MySQL对待这些表和普通表一样,但是这些“临时表”是没有任何索引的。

type

表示MySQL在表中找到所需行的方式,又称“访问类型”。type 字段比较重要, 它提供了判断查询是否高效的重要依据依据. 通过 type 字段, 我们判断此次查询是 全表扫描 还是 索引扫描 等等。
注:表格性能从上到下是最坏到最佳顺序

类型    说明
ALL Full Table Scan, 最坏的情况, MySQL将遍历全表以找到匹配的行
index 表示全索引扫描(full index scan), 和 ALL 类型类似, 只不过 ALL 类型是全表扫描, 而 index 类型则仅仅扫描所有的索引, 而不扫描数据.index 类型通常出现在: 所要查询的数据直接在索引树中就可以获取到, 而不需要扫描数据. 当是这种情况时, Extra 字段 会显示 Using index。见下方例子
range 表示使用索引范围查询, 通过索引字段范围获取表中部分数据记录. 这个类型通常出现在 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操作中。当 type 是 range 时, 那么 EXPLAIN 输出的 ref 字段为 NULL, 并且 key_len 字段是此次查询中使用到的索引的最长的那个索引长度。见下方例子
ref 哪些列或常量与key一起使用,被用于查找索引列上的值。此类型通常出现在多表的 join 查询, 针对于非唯一或非主键索引, 或者是使用了最左前缀规则索引的查询。见下方例子
eq-ref 最多只返回一条符合条件的记录。使用唯一性索引或主键查找时会发生 (高效)。此类型通常出现在多表的 join 查询, 表示对于前表的每一个结果, 都只能匹配到后表的一行结果. 并且查询的比较操作通常是 =, 查询效率较高。见下方例子
const 针对主键或唯一索引的等值查询扫描, 最多只返回一行数据. const 查询速度非常快, 因为它仅仅读取一次即可。见下方例子
system 表中只有一条数据, 这个类型是特殊的 const 类型.

type为index例子

mysql> EXPLAIN SELECT update_time FROM blog;
+----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | blog  | NULL       | index | NULL          | updatetime | 6       | NULL |   14 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

上面的例子中, 我们查询的 update_time 字段恰好是一个索引, 因此我们直接从索引中获取数据就可以满足查询的需求了, 而不需要查询表中的数据. 因此这样的情况下, type 的值是 index, 并且 Extra 的值是 Using index.

type为range例子

mysql> EXPLAIN SELECT * FROM blog WHERE blog_id BETWEEN 2 AND 8;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | blog  | NULL       | range | PRIMARY       | PRIMARY | 8       | NULL |    4 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

注:此处blog_id 为blog 表的主键

type为ref例子

mysql> EXPLAIN SELECT * FROM blog, sys_user WHERE blog.user_id = sys_user.id AND sys_user.id = 13;
+----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | sys_user | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | blog     | NULL       | ref   | userid        | userid  | 4       | const |   13 |   100.00 | NULL  |
+----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)

注:此处user_id为blog表里的一个索引字段(Normal 类型),sys_user表的主键为id。

type为eq-ref例子

mysql> EXPLAIN SELECT * FROM blog, sys_user WHERE blog.user_id = sys_user.id;
+----+-------------+----------+------------+--------+---------------+---------+---------+-------------------------+------+----------+-------+
| id | select_type | table    | partitions | type   | possible_keys | key     | key_len | ref                     | rows | filtered | Extra |
+----+-------------+----------+------------+--------+---------------+---------+---------+-------------------------+------+----------+-------+
|  1 | SIMPLE      | blog     | NULL       | ALL    | userid        | NULL    | NULL    | NULL                    |   14 |   100.00 | NULL  |
|  1 | SIMPLE      | sys_user | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | my_blog_db.blog.user_id |    1 |   100.00 | NULL  |
+----+-------------+----------+------------+--------+---------------+---------+---------+-------------------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)

type为const例子

mysql> EXPLAIN SELECT * FROM blog WHERE blog_id = 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | blog  | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

partitions

匹配的分区。

possible_keys

此次查询中可能选用的索引。
表示 MySQL 在查询时, 能够使用到的索引。注意, 即使有些索引在 possible_keys 中出现, 但是并不表示此索引会真正地被 MySQL 使用到。MySQL 在查询时具体使用了哪些索引, 由 key 字段决定。

key

此字段是 MySQL 在当前查询时所真正使用到的索引。如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key_len

表示索引中使用的字节数,如果键是NULL,则长度为NULL。这个字段可以评估组合索引是否完全被使用, 或只有最左部分字段被使用到。
key_len 的计算规则如下:

  • 字符串

    • char(n): n 字节长度
    • varchar(n): 如果是 utf8 编码, 则是 3 n + 2字节; 如果是 utf8mb4 编码, 则是 4 n + 2 字节。
  • 数值类型:

    • TINYINT: 1字节
    • SMALLINT: 2字节
    • MEDIUMINT: 3字节
    • INT: 4字节
    • BIGINT: 8字节
  • 时间类型

    • DATE: 3字节
    • TIMESTAMP: 4字节
    • DATETIME: 8字节
  • 字段属性: NULL 属性 占用一个字节. 如果一个字段是 NOT NULL 的, 则没有此属性。

粟子

mysql> EXPLAIN SELECT * FROM sys_user WHERE id < 13 and user_name = "admin" and email = "[email protected]";
+----+-------------+----------+------------+-------+---------------------------------+---------+---------+------+------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys                   | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------------------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | sys_user | NULL       | range | PRIMARY,id_username_email_index | PRIMARY | 4       | NULL |    1 |     5.00 | Using where |
+----+-------------+----------+------------+-------+---------------------------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

上面的例子是从表 sys_user 中查询指定的内容, 而从此表的建表语句中可以知道, 表 sys_user 有一个联合索引:

UNIQUE KEY `id_username_email_index` (`id`,`user_name`,`email`) USING BTREE

不过此查询语句 WHERE id < 13 and user_name = "admin" and email = "[email protected]" 中, 因为先进行 id 的范围查询, 而根据 最左前缀匹配 原则, 当遇到范围查询时, 就停止索引的匹配, 因此实际上我们使用到的索引的字段只有 id, 因此在 EXPLAIN 中, 显示的 key_len 为 4. 因为 id 字段是 int 占用 4 字节。上面因为 最左前缀匹配 原则, 我们的查询仅仅使用到了联合索引的 id 字段, 因此效率不算高。

更多关于key_len 的计算可以看看这篇文章:Explain之key_len长度计算

ref

哪个字段或常数与 key 一起被使用

rows

rows 也是一个重要的字段. MySQL 查询优化器根据统计信息, 估算 SQL 要查找到结果集需要扫描读取的数据行数。这个值非常直观显示 SQL 的效率好坏, 原则上 rows 越少越好。

filtered

表示此查询条件所过滤的数据的百分比。

Extra

包含MySQL解决查询的详细信息, MySQL查询优化器执行查询的过程中对查询计划的重要补充信息。
有以下几种情况:

  • Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序”,一般有 Using filesort, 都建议优化去掉, 因为这样的查询 CPU 资源消耗大。
  • Using index: 表示查询在索引树中就可查找所需数据, 不用扫描表数据文件, 往往说明性能不错
  • Using temporary: 用临时表保存中间结果,常用于GROUP BY 和 ORDER BY操作中,一般看到它说明查询需要优化了,就算避免不了临时表的使用也要尽量避免硬盘临时表的使用,查询效率不高。
  • Using where:仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤
  • Impossible WHERE noticed after reading const tables: 这个值强调了where语句会导致没有符合条件的行

猜你喜欢

转载自blog.csdn.net/CPOHUI/article/details/107575636