专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

MySQL explain 详解

功能

EXPLAIN 语句提供有关 MySQL 如何执行语句的信息:

  • EXPLAIN 支持 SELECTINSERTREPLACEUPDATE 语句,在 MYSQL 8.0.19 和更高的版本中,还支持 TABLE 语句。
  • EXPLAIN 与可解释的语句一起使用时,MYSQL 将会显示来自优化器的有关语句执行计划的信息。也就是说,MYSQL 解释了它将如何处理该语句,包括了有关如何连接表以及以何种顺序连接表的信息。
  • EXPLAINFOR CONNECTION connction_id 而不是可执行的语句一起使用时,它将显示在命名连接中执行的语句的执行计划。
  • 对于可解释的语句,EXPLAIN 会生成其他执行计划信息,这些信息可以使用 SHOW WARNINGS 显示。
mysql> show warnings;
+-------+------+------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                  |
+-------+------+------------------------------------------------------------------------------------------+
| Error | 3012 | EXPLAIN FOR CONNECTION command is supported only for SELECT/UPDATE/INSERT/DELETE/REPLACE |
+-------+------+------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

  • EXPLAIN 对于检查涉及分区表的查询很有用。
  • EXPLAIN 需要执行解释的语句所需的相同特权。此外,对于任何解释的视图,EXPLAIN 还需要具有 SHOW VIEW 特权。如果指定的连接属于其他用户,则 EXPLAIN ... FOR CONNECTION 也需要 PROCESS 特权。
  • EXPLAIN 的帮助下,您可以看到应该向表中添加索引的位置,以便通过使用索引查找行来使语句更快地执行。您还可以使用 EXPLAIN 来检查优化器是否以最佳顺序联接表。要提示优化器使用与 SELECT 语句中表的命名顺序相对应的连接顺序,请以 SELECT STRAIGHT_JOIN 而不是 SELECT 开头。

explain 输出列

EXPLAIN 的每个输出行都提供有关一个表的信息,

含义
id 该 SELECT 标识符
select_type 该 SELECT 类型
table 表名
partitions 匹配的分区
type JOIN 类型
possible_keys 可供选择的索引
key 实际选择的索引
key_len 所选索引的长度
ref 与索引进行比较的列
rows 估计要检查的行数
filtered 按查询条件过滤的行的百分比
Extra 附加信息

id

SELECT 标识符,查询的顺序编号。如果改行引用其他行 UNION 的结果,该值可以为 NULL

select_type

查询的类型,取值为下表中的任意一种。

select_type 值 含义
SIMPLE 简单 SELECT(不使用 UNION 或子查询)
PRIMARY 最外层 SELECT
UNION UNION 中第二个或之后的 SELECT 语句
DEPENDENT UNION UNION 中的第二个或之后的 SELECT 语句 ,取决于外部查询
UNION RESULT UNION 的结果
SUBQUERY 子查询中的第一个 SELECT
DEPENDENT SUBQUERY 子查询中的第一个 SELECT,取决于外部的查询
DERIVED 派生表
DEPENDENT DERIVED 依赖另一个表的派生表
MATERIALIZED 物化子查询
UNCACHEABLE SUBQUERY 结果集不能被缓存的子查询,必须重新评估外部查询的每一行
UNCACHEABLE UNION UNION 属于 UNCACHEABLE SUBQUERY 的第二个或后面的查询

table

输出行所引用的表的名称。有时候并不是真实的表名,可以是下列值之一:

  • <unionM,N>:该行引用 id 值为 MN 的行的并集。
  • <derivedN>:该行引用 id 值为 N 的行的派生表结果。例如,派生表可能来自 FROM 子句中的子查询。
  • <subqueryN>:该行引用 id 值为 N 的行的具体化物化子查询的结果。

partitions

其记录与查询匹配的分区,无分区表的值为 NULL。

type

type 列描述了表是如何连接的。下表描述了从最佳到最差的连接类型:

类型 介绍
system 该表只有一行(系统表)。这是 const 连接类型的特例。
const 该表最多有一个匹配行, 在查询开始时读取。 因为只有一行,所以优化器的其余部分可以将此行中列的值视为常量。 const 表非常快,因为它们只读一次。当您将 PRIMARY KEYUNIQUE 索引的所有部分与常量值进行比较时,将使用 const
eq_ref 当使用了索引的全部组成部分,并且索引是 PRIMARY KEYUNIQUE NOT NULL 才会使用该类型,性能仅次于 systemconst
ref 当满足索引的最左前缀规则,或者索引不是主键也不是唯一索引时才会发生。如果使用的索引只会匹配到少量的行,性能也是不错的。
fulltext 使用 FULLTEXT 索引进行连接。
ref_or_null 该类型类似于 ref,但是 MySQL 会额外搜索哪些行包含了 NULL。这种类型常见于解析子查询。
index_merge 此类型表示使用了索引合并优化,表示一个查询里面用到了多个索引。
unique_subquery 该类型和 eq_ref 类似,但是使用了 IN 查询,且子查询是主键或者唯一索引。
index_subquery unique_subquery 类似,只是子查询使用的是非唯一索引。
range 使用索引选择行,仅检索给定范围内的行。输出的行中 key 列指出使用的索引。key_len 包含使用过的最长的键部分。该类型的 ref 列值为 NULL。使用 =,<>,>,> =,<,<=,IS NULL,<=>,BETWEEN,LIKE 或 IN()运算符将 key 列与常量进行比较时,可以使用 range
index indexALL 区别为 index 类型只遍历索引树。如果索引是查询的覆盖索引,并且可用于满足表中所需的所有数据,则仅扫描索引树。在这种情况下,Extra 列显示 use index,索引扫描通常比 ALL 快,因为索引的大小通常小于表数据。按索引的顺序来查找数据行,执行了全表扫描。此时,explain的 Extra 列的结果不会出现 Uses index
all MySQL 将扫描全表以找到匹配的行。

possible_keys

指出 MySQL 可以从中选择查找此表中的行的索引,需要注意的是查询中不一定会使用到。

如果此列为 NULL,则没有相关的索引。

在这种情况下,你可以通过检查 WHERE 子句来检查它是否引用了某些适合的带有索引的列,从而改善查询性能。如果没有的话,应该创建一个合适的索引,然后再次使用 EXPLAIN 检查查询。

查看一个表中的索引命令如下:

show index from table_name;

key

key 列显示了 MYSQL 实际决定使用的索引。如果 MYSQL 决定使用 possible_keys 中的一个索引来检索行,则这个索引为 key 值。

key 值也可能是一个不在 possible_keys 中的索引。如果再 possible_keys 没有一个索引适合检索行,但是查询的所有列是其他索引的一些列,则可能发生这种情况。也就是说,命名索引覆盖了选定的列,因此尽管不使用索引来确定要检索的行,但索引扫描比数据行扫描更有效。

对于 InnoDB,即使查询也选择了主键,辅助索引也可能覆盖选定的列,因为 InnoDB 将主键值与每个辅助索引一起存储。如果 key 为 NULL,则 MySQL 未找到可用于更有效地执行查询的索引。

要强制 MySQL 使用或忽略 possible_keys 列中列出的索引,可以在查询中使用 FORCE INDEXUSE INDEXIGNORE INDEX

key_len

key_len 列表示 MySQL 实际使用的索引的长度。key_len 的值使你能够确定 MySQL 实际使用 multiple-part key 的多少部分。如果 key 列为 null, 则 key_len 列也为 null。

ref

表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

rows

rows 列表示 MySQL 认为必须检查以执行查询的行数。注意,对于 InnoDB 表来说,此值是估算值。

filtered

filtered 列表示按查询条件过滤的行的百分比。最大值为 100, 表示未发生行过滤。

Extra

此列包含有关 MySQL 如何解析查询的其他信息。下面的列表展示了该列可能的值:

Extra 值 含义
Child of ‘table’ pushed join@1 该表在联接中作为子表被引用。
const row not found 对于如下查询:SELECT ... FROM tbl_name,表是空的。
Deleting all rows 对于 DELETE,某些存储引擎(例如 MyISAM )支持一种处理方法,该方法以一种简单而快速的方式删除所有表行。如果引擎使用此优化,则会显示此 Extra 值。
Distinct MySQL 正在查找没有重复的值,在找到第一个匹配行后就会停止搜索更多的行。
FirstMatch(tbl_name) 为 tbl_name 表使用半联接 FirstMatch 联接快捷方式策略。
Full scan on NULL key 当优化器无法使用索引查找访问方法时,子查询优化作为回退策略会出现该值。
Impossible HAVING HAVING 子句一直是 false 并且无法选择任何行。
Impossible WHERE WHERE 子句一直是 false 并且无法选择任何行。
Impossible WHERE noticed after reading const tables MySQL 已经读取了所有 const (和 system) 表,并且注意到 WHERE 子句一直为 false。
LooseScan(m..n) 使用半联接 LooseScan 策略。m 和 n 是 key 的编号。
No matching min/max row 没有行满足条件查询,如 SELECT MIN(…) FROM … WHERE condition
no matching row in const table 对于具有联接的查询,有一个空表或没有满足唯一索引条件的行的表会出现该值。
No matching rows after partition pruning 对于 DELETE 和 UPDATE ,优化器在分区修剪(精简)之后找不到要删除或更新的内容。与 SELECT 语句的 Impossible WHERE 的含义相似。
No tables used 查询没有 FROM 子句,或者使用 FROM DUAL 子句。对于 INSERT 或 FRPLACE 语句,EXPLAIN 在没有 SELECT 部分时显示该值。例如,出现在 EXPLAIN INSERT INTO t VALUES(10) 中,该语句等同于 EXPLAIN INSERT INTO t SELECT 10 FROM DUAL
Not exists MySQL 能够对查询执行 LEFT JOIN 优化,并且在找到与 LEFT JOIN 条件匹配的行后,不会检查此表中其它的行。如下示例:SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE t2.id IS NULL;。假设 t2.id 被定义为 NOT NULL,MySQL扫描 t1 并使用 t1.id 的值在 t2 中查找行。 如果 MySQL 在t2中找到匹配的行,则它知道 t2.id 永远不能为 NULL,并且不会扫描 t2 中具有相同 id 值的其余行。换句话说,对于 t1 中的每一行,MySQL 需要在 t2 中只进行一次查找,而不管 t2 中实际匹配多少行。
Plan isn’t ready yet 执行计划还没还没创建完成。当优化器尚未完成为联接中的执行语句创建执行计划时,EXPLAIN FOR CONNECTION 会出现该值。
Range checked for each record (index map: N) MySQL 没有找到可用的好索引,但发现前面表中的列值是已知后,可能会使用某些索引。对于前面的表行,MySQL 检查是否可以使用 rangeindex_merge 访问方法来检索行,这不是很快,但比没有索引的联接快。
Recursive 表示该行适用于递归的 SELECT 递归通用表 表达式。
Rematerialize 在 T 表的 explain 行中,Rematerialize (X,...) 被显示,其中 X 是任何横向派生表,当读取 T 的新行时,其重新实现被触发。例如:SELECT ... FROM t, LATERAL (derived table that refers to t) AS dt。每当顶级查询处理新的 T 行时,都会重新实现派生表的内容,以使它保持最新。
Scanned N databases 表示在处理 INFORMATION_SCHEMA 表的查询时,服务器执行的目录扫描数。N 的值可以是0,1 或 ALL。
Select tables optimized away 优化器确定:1.最多只返回一行,2.若要生成此行,必须读取确定的行。在优化阶段(例如,通过读取索引行)可以获得要读取的行时,查询执行期间不需要读取任何表。当隐式分组查询(包含聚合函数,但不包含 group by 子句)时,将满足第一个条件。当每使用一个索引执行一行查找时,就满足了第二个条件。读取的索引数决定了要读取的行数。隐式分组查询示例:SELECT MIN(c1), MIN(c2) FROM t1;。c1 和 c2 都是索引列,并且该列是索引的第一列。可以通过读取一个索引行来检索 MIN(c1) ,并且通过从不同的索引中读取一行来检索 MIN(c2) 。本例中,返回的一行数据,是由读取两个确定性行生成的。如果读取的行不是确定的,则 Extra 不会显示此值,如下示例:SELECT MIN(c2) FROM t1 WHERE c1 <= 10;。假设(c1,c2)是覆盖索引。使用此索引,必须扫描 c1 <= 10的所有行以找到最小的c2值。相比之下,请考虑以下查询:SELECT MIN(c2) FROM t1 WHERE c1 = 10;。在这种情况下,具有 c1 = 10 的第一索引行包含最小 c2 值。仅一行必须读取才能产生返回的行。
Skip_open_table, Open_frm_only, Open_full_table 这此值表示适用于 INFORMATION_SCHEMA 表的查询的文件打开优化。Skip_open_table:不需要打开表文件。通过扫描数据库目录,该信息已在查询中可用。Open_frm_only:只需打开表的 .frm 文件。Open_full_table:未经优化的信息查找。必须打开 .frm,.MYD 和 .MYI 文件。
Start temporary, End temporary 临时表用于半联接 Duplicate Weedout 策略。
unique row not found 对于 SELECT …. FROM tbl_name 之类的查询,没有行满足表上 UNIQUE 索引 或 PRIMARY KEY 的条件。
Using filesort MySQL 必须执行额外的操作,以了解如何按排序顺序检索行。排序是根据联接类型遍历所有行,并为所有与 WHERE 子句匹配的行存储排序的 key 和 指向行的指针;然后对 key 进行排序,并按排序顺序检索行。
Using index 仅使用索引树中的信息从表中检索列信息。当查询公使用单个索引列时,显示此策略。对于具有用户定义的聚簇索引的 InnoDB 表,即使 Extra 列中不存在 use index,实际也是可以使用索引。如果 typeindex 并且 keyPRIMARY,则会出现此情况,即使用主键查询。
Using index condition 通过访问索引元组并首先测试它他来读取表,以确定是否读取全表的行。
Using index for group-by Using index 表访问方式相似。Using index for group-by 表示 MySQL 找到了一个索引,可用于检索 GROUP BYDISTINCT 查询所有的列,而无需对实际的表进行任何额外的磁盘访问。此外,索引以最有效的方式使用,因此,对每个 group,只需读取少数的索引。
Using index for skip scan 表示使用跳过扫描访问方法。
Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access), Using join buffer (hash join) 将早期联接中的表部分读入联接缓冲区,然后从缓冲区中使用他们的行来执行与当前表的联接。也就是说,将缓冲 explain 输出前一行上表中的 key,并从使用 join buffer 的行指向的表中分批提取匹配的行。
Using MRR 使用 Multi-Range Read 优化策略读取表。
Using sort_union(…), Using union(…), Using intersect(…) 这些指示了特定算法,该算法显示了如何针对 index_merge 连接类型合并索引扫描。
Using temporary 要对查询进行解析,MySQL 需要创建一个临时表来保存结果。如果查询包含多个不同列的 GROUP BY 和ORDER BY 子句,则通常会发生此情况。
Using where WHERE 子句用于限制那些行与下一个表匹配或发送到客户端。除非特意要检索表中的所有行,否则若 Extra 值不是 Using where 并且表联接是 ALLindex,则可能出错了。
Using where with pushed condition 此项只适用于 NDB 表。意思是 NDB Cluster 正在使用下推优化来提高非索引列和常量之间直接比较的效率。
Zero limit 该查询具有 LIMIT 0 子句,并且无法选择任何行。

explain for connection

语法

EXPLAIN [options] FOR CONNECTION connection_id;

connection_id

CONNECTION_ID 是连接标识符,可以从 INFORMATION_SCHEMA.PROCESSLIST 表或通过 SHOW PROCESSLIST 语句获得。如果你具有 PROCESS 特权,则可以为任何连接指定标识符,否则,你只能为自己的连接指定标识符。

获取 CONNECTION_ID 的两种方式:

mysql> show processlist;
+----+-----------------+-----------+----------+---------+--------+------------------------+------------------+
| Id | User            | Host      | db       | Command | Time   | State                  | Info             |
+----+-----------------+-----------+----------+---------+--------+------------------------+------------------+
|  4 | event_scheduler | localhost | NULL     | Daemon  | 767873 | Waiting on empty queue | NULL             |
| 13 | root            | localhost | example2 | Sleep   |   1177 |                        | NULL             |
| 14 | root            | localhost | example  | Query   |      0 | starting               | show processlist |
+----+-----------------+-----------+----------+---------+--------+------------------------+------------------+
3 rows in set (0.00 sec)

mysql> use INFORMATION_SCHEMA;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select * from processlist;
+----+-----------------+-----------+--------------------+---------+--------+------------------------+---------------------------+
| ID | USER            | HOST      | DB                 | COMMAND | TIME   | STATE                  | INFO                      |
+----+-----------------+-----------+--------------------+---------+--------+------------------------+---------------------------+
|  4 | event_scheduler | localhost | NULL               | Daemon  | 767907 | Waiting on empty queue | NULL                      |
| 13 | root            | localhost | example2           | Sleep   |   1211 |                        | NULL                      |
| 14 | root            | localhost | information_schema | Query   |      0 | executing              | select * from processlist |
+----+-----------------+-----------+--------------------+---------+--------+------------------------+---------------------------+
3 rows in set (0.00 sec)

功能

EXPLAIN FOR CONNECTION 会返回当前在给定连接中用于执行查询的 EXPLAIN 信息。由于数据(和支持统计数据)的更改,它可能会与在等效查询文本上运行EXPLAIN产生不同的结果。行为上的这种差异对于诊断更多瞬时性能问题很有用。例如,如果您在一个会话中运行需要很长时间才能完成的语句,则在另一个会话中使用EXPLAIN FOR CONNECTION可能会产生有关延迟原因的有用信息。

限制

1、 如果选择的连接中无执行语句,则结果为空。

mysql> use example;

mysql> explain for connection 13;
Query OK, 0 rows affected (0.00 sec)

1、 不能检查没有执行 DML 语句的连接(SELECT,DELETE,INSERT,REPLACE 和 UPDATE),否则会报错。同样,不能检查自身的连接

mysql> explain for connection 14;
ERROR 3012 (HY000): EXPLAIN FOR CONNECTION command is supported only for SELECT/UPDATE/INSERT/DELETE/REPLACE

1、 只能检查正在执行的 SQL,即使的刚刚执行过的 SQL 也检查不了。

数据量多、执行时间长的可以试一试。

注:在 mysql 的提示符里面加上当前时间,可以用:prompt \r:\m:\s\P>_

explain type 字段详解

type 扫描类型由快到慢:system > const > eq_ref > ref > range > index > ALL

测试表结构如下:

CREATE TABLE `goods` (
  `goods_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '商品id',
  `goods_name` varchar(128) NOT NULL DEFAULT '' COMMENT '商品名',
  `category_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '分类id',
  `category_name` varchar(128) NOT NULL DEFAULT '' COMMENT '分类名',
  `shop_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '店铺id',
  `shop_name` varchar(128) NOT NULL DEFAULT '' COMMENT '店铺名',
  PRIMARY KEY (`goods_id`),
  KEY `idx_category_id` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表';

INSERT INTO `goods` (`goods_id`, `goods_name`, `category_id`, `category_name`, `shop_id`, `shop_name`) VALUES
(1, '男鞋1',  1,  '鞋',    12, '淘宝'),
(2, '男鞋2',  1,  '鞋',    12, '淘宝'),
(3, '男鞋3',  3,  '', 13, '京东'),
(4, 'T恤1',  2,  'T恤',   13, '京东'),
(5, 'T恤2',  2,  'T恤',   14, '拼多多');

CREATE TABLE `category` (
  `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '分类id',
  `category_name` varchar(128) NOT NULL DEFAULT '' COMMENT '分类名',
  PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品分类表';

INSERT INTO `category` (`category_id`, `category_name`) VALUES
(1, '鞋'),
(2, 'T恤'),
(3, '羽绒服');

CREATE TABLE `shop` (
  `shop_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '店铺id',
  `shop_name` varchar(128) NOT NULL DEFAULT '' COMMENT '店铺名',
  PRIMARY KEY (`shop_id`),
  KEY `idx_shop_name` (`shop_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='店铺表';

INSERT INTO `shop` (`shop_id`, `shop_name`) VALUES
(13,    '京东'),
(11,    '天猫'),
(14,    '拼多多'),
(12,    '淘宝');

system 详解

场景

官方文档介绍的是表中只有一条记录的系统表,explain 的 type 为该类型,但亲测当引擎为 MyISAM 时,explain 结果 type 为 system,当引擎为 InnoDB 时,explain 结果 type 为 ALL,以 mysql.proxies_priv 表为例:

示例

mysql> show create table mysql.proxies_priv;

CREATE TABLE `proxies_priv` (
  `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
  `User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
  `Proxied_host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
  `Proxied_user` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
  `With_grant` tinyint(1) NOT NULL DEFAULT '0',
  `Grantor` char(93) COLLATE utf8_bin NOT NULL DEFAULT '',
  `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`Host`,`User`,`Proxied_host`,`Proxied_user`),
  KEY `Grantor` (`Grantor`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='User proxy privileges'

mysql> explain select * from mysql.proxies_priv;
+----+-------------+--------------+------------+--------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table        | partitions | type   | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+--------------+------------+--------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | proxies_priv | NULL       | system | NULL          | NULL | NULL    | NULL |    1 |   100.00 | NULL  |
+----+-------------+--------------+------------+--------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> show create table mysql.proxies_priv;

CREATE TABLE `proxies_priv` (
  `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
  `User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
  `Proxied_host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
  `Proxied_user` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
  `With_grant` tinyint(1) NOT NULL DEFAULT '0',
  `Grantor` char(93) COLLATE utf8_bin NOT NULL DEFAULT '',
  `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`Host`,`User`,`Proxied_host`,`Proxied_user`),
  KEY `Grantor` (`Grantor`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='User proxy privileges'

mysql> explain select * from mysql.proxies_priv;
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table        | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | proxies_priv | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | NULL  |
+----+-------------+--------------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

const 详解

场景

type 为 const 的一般场景为:

1、 命中 主键索引唯一索引
2、 被连接的部分是一个 常量

示例

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

eq_ref 详解

场景

type 为 eq_ref 的场景一般出现在 左连接右连接 中,下面例子中对于 goods 表中的每一行,对应后面的表 goods_category 中只有一行被扫描。

eq_ref 出现的场景一般是:

1、 联表(join) 查询
2、 命中 主键 或者 非空唯一索引
3、 等值 连接

示例

mysql> explain select * from goods left join category on goods.category_id = category.category_id;
+----+-------------+----------+------------+--------+---------------+---------+---------+---------------------------+------+----------+-------------+
| id | select_type | table    | partitions | type   | possible_keys | key     | key_len | ref                       | rows | filtered | Extra       |
+----+-------------+----------+------------+--------+---------------+---------+---------+---------------------------+------+----------+-------------+
|  1 | SIMPLE      | goods    | NULL       | ALL    | NULL          | NULL    | NULL    | NULL                      |    5 |   100.00 | NULL        |
|  1 | SIMPLE      | category | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | example.goods.category_id |    1 |   100.00 | Using where |
+----+-------------+----------+------------+--------+---------------+---------+---------+---------------------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

ref 详解

场景

refeq_ref 类似,不过有一点区别,eq_ref 命中的是 主键或非空索引,而 ref 命中的是 普通不唯一索引

因此,ref 出现的场景为:

  • 使用 普通索引,查询 常量

示例

mysql> explain select * from goods left join shop on goods.shop_name = shop.shop_name;
+----+-------------+-------+------------+------+---------------+---------------+---------+-------------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key           | key_len | ref                     | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+---------------+---------+-------------------------+------+----------+-------------+
|  1 | SIMPLE      | goods | NULL       | ALL  | NULL          | NULL          | NULL    | NULL                    |    5 |   100.00 | NULL        |
|  1 | SIMPLE      | shop  | NULL       | ref  | idx_shop_name | idx_shop_name | 514     | example.goods.shop_name |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+---------------+---------+-------------------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

由于 后表 shop 使用了 普通非唯一索引,对于前表 goods 的每一行,后表 shop 都有可能查询不止一条。

mysql> explain select * from shop where shop_name = '拼多多';
+----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key           | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | shop  | NULL       | ref  | idx_shop_name | idx_shop_name | 514     | const |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+---------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

普通查询也是如此,当使用 普通索引 查询的时候,typeref

ref_or_null

range 详解

场景

range 比较好理解,它是索引上的范围查询,它会在索引上扫码特定范围内的值。常用的范围有 between,in,>,>=,<,<=

示例

mysql> explain select * from goods where goods_id > 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | goods | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    4 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

index 详解

index 是遍历索引树查询,比如我们查询表中的一个索引字段,这个时候就是遍历索引树。

示例

mysql> explain select category_id from goods;
+----+-------------+-------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key             | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | goods | NULL       | index | NULL          | idx_category_id | 8       | NULL |    5 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

all 详解

如何查询的字段上没有索引,那么 type 就会为 all

示例

mysql> explain select * from goods where shop_name = '淘宝';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | goods | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |    20.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

参考文档

  • [8.8.4 Obtaining Execution Plan Information for a Named Connection][]
  • [13.8.2 EXPLAIN Statement][]
  • [8.8.2 EXPLAIN Output Format][]

文章永久链接:https://tech.souyunku.com/45073

未经允许不得转载:搜云库技术团队 » MySQL explain 详解

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们