理解MySQL中的explain

前言

我们可以使用explain命令来查看MySQL查询优化器的执行计划是怎么来优化查询的。通过结果反馈,我们能更好地选择索引,同时也能写出更好的查询语句。

【注】:本节我们使用的MySQL版本为5.7.11。

语法

explain命令可作用于具体的表。也可作用于DML语句,包括SELECTDELETEINSERTREPLACEUPDATE

当作用于具体表时,和desc命令效果相同,用于描述表的信息,结果信息包括:字段名称、字段类型、是否为null、是否主键、默认值等等。

-- explain作用于表
explain `{tableName}`;

当作用于DML语句时,可用于查看执行计划。

  1. 因为REPLACEINSERT语句相对来说较为简单,本节不会额外分析他们。
  2. DELETEUPDATESELECT有相似之处,都可以写WHERE条件,所以本节仅以SELECT为例来进行分析。
-- explain作用于DML
explain DML语句;

列信息详解

我们先给出最简单的例子,然后对例子中的每一列进行详细分析。所有的分析都借鉴了官方说明

最简单的例子

我们创建一个user表,其DDL如下。

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `username` varchar(20) NOT NULL COMMENT '用户名',
  `password` varchar(20) NOT NULL COMMENT '密码',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

执行explain之后,我们得到如下结果。

mysql> explain select * from user where id = 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | user  | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

1. id

id列可以是一个正整数或null,且满足下面的规则。

  1. id大的先执行
  2. id相同的,按照顺序从上往下执行
  3. id为null表示是一个结果集,不是一个查询

2. select_type

用于表示查询中每个SELECT子句的类型,可能出现的枚举值如下

1. SIMPLE
2. PRIMARY
3. UNION
4. DEPENDENT UNION
5. UNION RESULT
6. SUBQUERY
7. DEPENDENT SUBQUERY
8. DERIVED
9. UNCACHEABLE SUBQUERY
10. UNCACHEABLE UNION

2.1.SIMPLE

简单查询,子查询和UNION查询之外的其他查询。

mysql> explain select * from user where id = 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | user  | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

2.2. PRIMARY

含有子查询或者UNION查询时,最外层或者最远的查询。

mysql> explain select username from user a where id = 1 union select username from user b  where id = 3;
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| id | select_type  | table      | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra           |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
|  1 | PRIMARY      | a          | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL            |
|  2 | UNION        | b          | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL            |
| NULL | UNION RESULT | <union1,2> | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)

2.3. UNION

UNION语句中的第二个或更后面的查询子句,第一个为PRIMARY

mysql> explain select username from user a where id = 1 union select username from user b  where id = 3;
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| id | select_type  | table      | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra           |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
|  1 | PRIMARY      | a          | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL            |
|  2 | UNION        | b          | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL            |
| NULL | UNION RESULT | <union1,2> | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)

2.4. DEPENDENT UNION

和UNION类似,只是表示外部查询需要对其进行依赖。比如下面的查询,会先查询id列值为2和3的记录,然后纳入select * from user的查询条件中。

mysql> explain select * from user where username in (select username from user a where id = 1 union select username from user b  where id = 3);
+----+--------------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| id | select_type        | table      | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra           |
+----+--------------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
|  1 | PRIMARY            | user       | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  |    3 |   100.00 | Using where     |
|  2 | DEPENDENT SUBQUERY | a          | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL            |
|  3 | DEPENDENT UNION    | b          | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL            |
| NULL | UNION RESULT       | <union2,3> | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  | NULL |     NULL | Using temporary |
+----+--------------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
4 rows in set, 1 warning (0.00 sec)

2.5. UNION RESULT

用于表示UNION的结果,因为不需要参与查询,所以id列为null。

mysql> explain select * from user where username in (select username from user a where id = 1 union select username from user b  where id = 3);
+----+--------------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| id | select_type        | table      | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra           |
+----+--------------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
|  1 | PRIMARY            | user       | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  |    3 |   100.00 | Using where     |
|  2 | DEPENDENT SUBQUERY | a          | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL            |
|  3 | DEPENDENT UNION    | b          | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL            |
| NULL | UNION RESULT       | <union2,3> | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  | NULL |     NULL | Using temporary |
+----+--------------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
4 rows in set, 1 warning (0.00 sec)

2.6. SUBQUERY

子查询里面的第一个SELECT子句。

mysql> explain select password from user a where id = (select id from user b  where id = 3);
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | PRIMARY     | a     | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL        |
|  2 | SUBQUERY    | b     | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

2.7. DEPENDENT SUBQUERY

子查询内部的第一个查询子句,同时外层查询依赖子查询。

mysql> explain select update_time from user_extra as ue where exists (select id from user where user.id = ue.user_id and id = 1);
+----+--------------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type        | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+--------------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | PRIMARY            | ue    | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  |    1 |   100.00 | Using where |
|  2 | DEPENDENT SUBQUERY | user  | NULL       | const | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | Using index |
+----+--------------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec)

2.8. DERIVED

表示派生关系,在FROM子句里面有子查询,且子查询带分组时,会出现此种情况。

mysql> explain SELECT id FROM (SELECT id FROM user GROUP BY id) AS tmp;
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL   | NULL          | NULL    | NULL    | NULL |    3 |   100.00 | NULL        |
|  2 | DERIVED     | user       | NULL       | index | PRIMARY       | PRIMARY | 8       | NULL |    3 |   100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

2.9. UNCACHEABLE SUBQUERY和UNCACHEABLE UNION

主要出现在子查询有随机函数和UNION有随机函数的场景

3. table

该列表示被查询的表名,可能出现下面的值

  1. <unionM,N> // select_type为union,且union关联的id为M和N
  2. <derivedN> // select_type为derived,且是从id为N的结果导出的
  3. <subqueryN> // select_type为subquery,且是id为N的子查询

4. type

该列表示MySQL查询到具体数据行的方式,通常我们将其叫做访问类型,其值从优到差分别如下:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

  1. system & const

当用到主键索引或唯一索引时其值为const。system是const的特例,当查询系统表,且系统表里面只有一行数据时出现。

explain select * from user;
  1. eq_ref

主键索引或唯一索引作为两个表的连接方式时,通常和=一起使用。

explain select * from user, user_extra where user.id = user_extra.user_id;
  1. ref

用到了索引,但不是主键索引和唯一索引。

explain select * from user where uuid = 'x';
  1. ref_or_null

类似于ref,但是可以查询包含null的行,通常用在子查询里面。

  1. index_merge

当使用多个条件进行交集或并集时出现。


  1. range

对索引列进行范围查找时出现,比如betweenin><

explain select * from user where id > 1;
  1. index

全表扫描,但是只查询索引列的值,因此只需要扫描索引树即可。

explain select id from user;
  1. ALL

全表扫描,在服务端进行逻辑处理。性能最差的一种方式。

explain select * from user;

5. possible_keys

指出MySQL可能用到的索引字段,它会按照顺序,把所有可能涉及到的索引都列出来,但并不是每个索引都会用到。

6. keys

指出MySQL实际用到的索引字段。

7. key_len

表示使用的索引最大长度。在组合索引的情况下,该字段使用,罗列出每个索引字段的长度。需要注意的是,key_len只是表结构定义的翻译。在同样的条件下,key_len越短,查询的效率越高。

8. ref

ref显示哪些列或者常量被用于和索引列key进行对比。如果值为func,则表示此值是通过函数计算得出。

9. rows

Mysql认为需要查询的行数。

10. filtered

该列给出了一个百分比的值,这个百分比值和rows列的值一起使用,可以估计出那些将要和QEP中的前一个表进行连接的行的数目。前一个表就是指id列的值比当前表的id小的表。

11. extra

该列包含对于MySQL查询的附加描述信息,当该列出现Using filesort或者Using temporary时,我们必须对我们的查询语句进行优化。

参考链接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Mysql概述 数据库是一个易于访问和修改的信息集合。它允许使用事务来确保数据的安全性和一致性,并能快速处理百万条...
    彦帧阅读 13,654评论 10 461
  • 转自:http://blog.chinaunix.net/uid-540802-id-3419311.html e...
    小陈阿飞阅读 1,130评论 0 2
  • explain关键字可以模拟优化器执行SQL语句,从而知道MySQL是 如何处理你的SQL语句的。分析你的查询语句...
    Chting阅读 1,525评论 0 2
  • 转 # https://www.cnblogs.com/easypass/archive/2010/12/ 08/...
    吕品㗊阅读 9,695评论 0 44
  • MYSQL 基础知识 1 MySQL数据库概要 2 简单MySQL环境 3 数据的存储和获取 4 MySQL基本操...
    Kingtester阅读 7,772评论 5 116