EXPLAN
根据表、列、索引和WHERE子句中的条件的详细信息,MySQL 优化器会考虑许多技术来有效地执行 SQL 查询中涉及的查找。可以在不读取所有行的情况下执行对大表的查询;可以在不比较每个行组合的情况下执行涉及多个表的连接。优化器选择执行最高效查询的一组操作称为“查询执行计划”,也称为 EXPLAIN计划。你的目标是认识到 EXPLAIN 表明查询优化良好的计划,并学习 SQL 语法和索引技术以在您看到一些低效操作时改进计划。
NOTE
在较旧的 MySQL 版本中,分区和扩展信息是使用 EXPLAIN PARTITIONS和 生成的 EXPLAIN EXTENDED。这些语法仍然被识别为向后兼容,但分区和扩展输出现在默认启用,因此PARTITIONS 和 EXTENDED 关键字是多余的并且已弃用。使用它们会导致警告;希望EXPLAIN在未来的 MySQL 版本中将它们从语法中删除。
不能在同一语句中同时使用 deprecated PARTITIONS 和EXTENDED关键字 EXPLAIN。此外,这两个关键字都不能与FORMAT选项一起使用 。
NOTE
MySQL Workbench 具有 Visual Explain 功能,可提供EXPLAIN输出的可视化表示 。
属性
| 列 | JSON NAME | 意义 |
|---|---|---|
| id | select_id | 该SELECT标识符 |
| select_type | 不输出 | 该SELECT类型 |
| table | table_name | 输出行的表 |
| partitions | partitions | 匹配的分区 |
| type | access_type | 联接类型 |
| possible_keys | possible_keys | 可供选择的可能索引 |
| key | key | 实际选择的索引 |
| key_len | key_length | 所选密钥的长度 |
| ref | ref | 与索引比较的列 |
| rows | rows | 估计要检查的行数 |
| filtered | filtered | 按表条件过滤的行百分比 |
| Extra | 不输出 | 附加信息 |
id(JSON Name: select_id)
SELECT标识符。这是SELECT查询中的序列号 。如果该行引用其他行的联合结果,则该值可能是NULL。在这种情况下,该 table列显示的值类似于 表示该行引用具有 和值的行的 并集 。
select_type (JSON Name:无)
SELECT的类型,可以是下表中的任何一种。JSON 格式EXPLAIN将SELECT类型公开 为 a 的属性 query_block,除非它是 SIMPLE或PRIMARY。JSON Name称(如果适用)也显示在表中。
| select_type value | JSON Name | 意义 |
|---|---|---|
| SIMPLE | 无值 | 简单SELECT(不使用 UNION或子查询) |
| PRIMARY | 无值 | 最外面 SELECT |
| UNION 无值 | 中的第二个或以后的SELECT语句 UNION | |
| DEPENDENT UNION | dependent( true) | a 中的第二个或后面的SELECT语句 UNION,取决于外部查询 |
| UNION RESULT | union_result | 的结果UNION。 |
| SUBQUERY | 无值 | 首先SELECT在子查询 |
| DEPENDENT SUBQUERY | dependent( true) | 首先SELECT在子查询中,依赖于外部查询 |
| DERIVED | 无值 | 派生表 |
| MATERIALIZED | materialized_from_subquery | 物化子查询 |
| UNCACHEABLE SUBQUERY | cacheable( false) | 无法缓存结果并且必须为外部查询的每一行重新评估的子查询 |
| UNCACHEABLE UNION | cacheable( false) | UNION 属于不可缓存子查询的第二个或以后的选择(请参阅 UNCACHEABLE SUBQUERY) |
- DEPENDENT通常表示使用相关子查询。
- DEPENDENT SUBQUERY评估不同于UNCACHEABLE SUBQUERY评估。对于DEPENDENT SUBQUERY,对于来自其外部上下文的变量的每组不同值,子查询仅重新评估一次。对于 UNCACHEABLE SUBQUERY,为外部上下文的每一行重新评估子查询。
- 当您使用EXPLAIN时候指定FORMAT=JSON时 ,输出没有直接等效于select_type; 的单个属性 。该 query_block属性对应于给定的SELECT。与SELECT刚刚显示的大多数子查询类型等效的属性可用(例如 materialized_from_subqueryfor MATERIALIZED),并在适当时显示。SIMPLE或没有 JSON 等价物 PRIMARY。
- select_type非SELECT语句 的值显示受影响表的语句类型。例如,select_type 是 DELETE DELETE语句。
table (JSON name: table_name)
输出行所引用的表的名称。这也可以是以下值之一:
- <unionM,N>: 行是指具有 和id值的行 的 M并集 N。
:该行是指用于与该行的派生表结果id的值 N。例如,派生表可能来自FROM子句中的子查询 。 :该行是指与物化子查询该行的结果id 的值N。请参阅 第 8.2.2.2 节,“使用实现优化子查询”。
partitions(JSON Name: partitions)
查询将匹配记录的分区。该值NULL用于非分区表。
type(JSON Name: access_type)
联接类型。有关不同类型的说明,请参阅 EXPLAIN 联接类型。
possible_keys(JSON Name: possible_keys)
- 该possible_keys列指示 MySQL 可以选择从中查找该表中行的索引。请注意,此列完全独立于 EXPLAIN. 这意味着某些键possible_keys在实际中可能无法与生成的表顺序一起使用。
- 如果此列是NULL(或在 JSON 格式的输出中未定义),则没有相关索引。在这种情况下,您可以通过检查WHERE 子句来检查它是否引用了适合编制索引的某些列或多列,从而提高查询的性能。如果是这样,请创建适当的索引并EXPLAIN再次检查查询 。见 第 13.1.8 节,“ALTER TABLE 语句”。
- 要查看表具有哪些索引,请使用. SHOW INDEX FROM tbl_name
key(JSON Name:key)
该key列表示 MySQL 实际决定使用的键(索引)。如果 MySQL 决定使用其中一个possible_keys 索引来查找行,则该索引将作为键值列出。
可以key命名值中不存在的索引 possible_keys。如果没有任何possible_keys索引适合查找行,但查询选择的所有列都是某个其他索引的列,就会发生这种情况。也就是说,命名索引覆盖了选定的列,因此虽然它不用于确定要检索哪些行,但索引扫描比数据行扫描更有效。
对于InnoDB,二级索引可能会覆盖选定的列,即使查询也选择了主键,因为InnoDB将主键值与每个二级索引一起存储。如果 key是NULL,则 MySQL 找不到可用于更有效地执行查询的索引。
要强制MySQL使用或忽略列出的索引 possible_keys列,使用 FORCE INDEX,USE INDEX或IGNORE INDEX在您的查询。见第 8.9.4 节,“索引提示”。
对于MyISAM表,运行 ANALYZE TABLE有助于优化器选择更好的索引。对于 MyISAM表,myisamchk –analyze执行相同的操作。请参阅 第 13.7.2.1 节,“ANALYZE TABLE 语句”和 第 7.6 节,“MyISAM 表维护和崩溃恢复”。
key_len(JSON Name: key_length)
该key_len列表示 MySQL 决定使用的键的长度。的值 key_len使您能够确定 MySQL 实际使用的多部分键的多少部分。如果key列说 NULL,key_len 列也说NULL。
由于密钥存储格式的原因,列的密钥长度NULL 比列的长度NOT NULL大一。
ref(JSON Name:ref)
该ref列显示哪些列或常量与列中指定的索引进行比较以 key从表中选择行。
如果值为func,则使用的值是某个函数的结果。要查看哪个功能,请使用 SHOW WARNINGS以下内容 EXPLAIN查看扩展 EXPLAIN输出。该函数实际上可能是一个运算符,例如算术运算符。
rows(JSON Name: rows)
该rows列表示 MySQL 认为它必须检查以执行查询的行数。
对于InnoDB表格,这个数字是一个估计值,可能并不总是准确的。
filtered(JSON Name: filtered)
该filtered列指示按表条件过滤的表行的估计百分比。最大值为 100,这意味着没有发生行过滤。从 100 开始减小的值表示过滤量增加。 rows显示检查的估计行数,rows× filtered显示与下表连接的行数。例如,如果 rows是 1000 和 filtered50.00 (50%),则与下表连接的行数为 1000 × 50% = 500。
Extra (JSON Name称:无)
此列包含有关 MySQL 如何解析查询的附加信息。有关不同值的说明,请参阅 EXPLAIN 额外信息。
没有与Extra列对应的单个 JSON 属性 ;但是,此列中可能出现的值会作为 JSON 属性或作为属性的文本公开message。
EXPLAIN Join Types
该type列 EXPLAIN输出介绍如何联接表。在 JSON 格式的输出中,这些作为access_type属性的值被找到。下面的列表描述了连接类型,从最好的类型到最差的类型:
system
该表只有一行(= 系统表)。这是const连接类型的一个特例 。
const
该表最多有一个匹配行,在查询开始时读取。因为只有一行,该行中该列的值可以被优化器的其余部分视为常量。 const表非常快,因为它们只被读取一次。
const用于将 aPRIMARY KEY或 UNIQUE索引的所有部分与常量值进行比较。在以下查询中,tbl_name可以用作const 表:
1 | SELECT * FROM tbl_name WHERE primary_key=1; |
eq_ref
对于前面表中的每个行组合,从该表中读取一行。除了 systemand const类型之外,这是最好的连接类型。当连接使用索引的所有部分并且索引是一个 PRIMARY KEY或UNIQUE NOT NULL索引时使用它。
eq_ref可用于使用=运算符进行比较的索引列 。比较值可以是常量或表达式,该表达式使用在此表之前读取的表中的列。在以下示例中,MySQL 可以使用 eq_ref连接来处理 ref_table:
1 | SELECT * FROM ref_table,other_table |
ref
对于先前表中的每个行组合,从该表中读取具有匹配索引值的所有行。ref如果联接仅使用键的最左前缀或键不是 aPRIMARY KEY或 UNIQUE索引(换句话说,如果联接无法根据键值选择单行),则使用。如果使用的键只匹配几行,这是一个很好的连接类型。
ref可用于使用=or<=> 运算符进行比较的索引列 。在以下示例中,MySQL 可以使用 ref连接来处理 ref_table:
1 |
|
fulltext
连接是使用FULLTEXT 索引执行的。
ref_or_null
这种连接类型类似于 ref,但另外,MySQL 会额外搜索包含NULL值的行。这种连接类型优化最常用于解析子查询。在以下示例中,MySQL 可以使用 ref_or_null连接来处理ref_table:
1 | SELECT * FROM ref_table |
index_merge
此连接类型表示使用了索引合并优化。在这种情况下,key输出行中的列包含所使用索引的列表,并key_len包含所使用索引 的最长关键部分的列表。有关更多信息,请参阅 第 8.2.1.3 节,“索引合并优化”。
unique_subquery
这种类型替代 了以下形式的eq_ref一些 IN子查询:
1 | value IN (SELECT primary_key FROM single_table WHERE some_expr) |
unique_subquery 只是一个索引查找函数,完全替换子查询以提高效率。
index_subquery
这种联接类型类似于 unique_subquery. 它取代了IN子查询,但它适用于以下形式的子查询中的非唯一索引:
1 | value IN (SELECT key_column FROM single_table WHERE some_expr) |
range
仅检索给定范围内的行,使用索引来选择行。的key 输出行中的列指示使用哪个索引。将key_len包含已使用的时间最长的关键部分。该ref列适用 NULL于这种类型。
range当使用=, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE, 或 IN()运算符中的任何一个将键列与常量进行比较时,可以使用 :
1 | SELECT * FROM tbl_name |
index
该index联接类型是一样的 ALL,只是索引树被扫描。这有两种方式:
如果索引是查询的覆盖索引,可以满足表中所有需要的数据,则只扫描索引树。在这种情况下,该Extra列显示 Using index。仅索引扫描通常比ALL索引的大小通常小于表数据的大小要快 。
使用从索引中读取来执行全表扫描以按索引顺序查找数据行。 Uses index不会出现在 Extra列中。
当查询仅使用属于单个索引的列时,MySQL 可以使用此连接类型。
ALL
对先前表中的每个行组合进行全表扫描。如果该表是第一个未标记的表 const,这通常不好,并且在所有其他情况下通常 非常糟糕。通常,您可以ALL通过添加索引来避免 基于常量值或早期表中的列值从表中检索行。