MySql之EXPLAN详解

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
2
3
4
SELECT * FROM tbl_name WHERE primary_key=1;

SELECT * FROM tbl_name
WHERE primary_key_part1=1 AND primary_key_part2=2;

eq_ref

对于前面表中的每个行组合,从该表中读取一行。除了 systemand const类型之外,这是最好的连接类型。当连接使用索引的所有部分并且索引是一个 PRIMARY KEY或UNIQUE NOT NULL索引时使用它。

eq_ref可用于使用=运算符进行比较的索引列 。比较值可以是常量或表达式,该表达式使用在此表之前读取的表中的列。在以下示例中,MySQL 可以使用 eq_ref连接来处理 ref_table:

1
2
3
4
5
6
SELECT * FROM ref_table,other_table
WHERE ref_table.key_column=other_table.column;

SELECT * FROM ref_table,other_table
WHERE ref_table.key_column_part1=other_table.column
AND ref_table.key_column_part2=1;

ref

对于先前表中的每个行组合,从该表中读取具有匹配索引值的所有行。ref如果联接仅使用键的最左前缀或键不是 aPRIMARY KEY或 UNIQUE索引(换句话说,如果联接无法根据键值选择单行),则使用。如果使用的键只匹配几行,这是一个很好的连接类型。

ref可用于使用=or<=> 运算符进行比较的索引列 。在以下示例中,MySQL 可以使用 ref连接来处理 ref_table:

1
2
3
4
5
6
7
8
9

SELECT * FROM ref_table WHERE key_column=expr;

SELECT * FROM ref_table,other_table
WHERE ref_table.key_column=other_table.column;

SELECT * FROM ref_table,other_table
WHERE ref_table.key_column_part1=other_table.column
AND ref_table.key_column_part2=1;

fulltext

连接是使用FULLTEXT 索引执行的。

ref_or_null

这种连接类型类似于 ref,但另外,MySQL 会额外搜索包含NULL值的行。这种连接类型优化最常用于解析子查询。在以下示例中,MySQL 可以使用 ref_or_null连接来处理ref_table:

1
2
SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL;

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
2
3
4
5
6
7
8
9
10
11
SELECT * FROM tbl_name
WHERE key_column = 10;

SELECT * FROM tbl_name
WHERE key_column BETWEEN 10 and 20;

SELECT * FROM tbl_name
WHERE key_column IN (10,20,30);

SELECT * FROM tbl_name
WHERE key_part1 = 10 AND key_part2 IN (10,20,30);

index

该index联接类型是一样的 ALL,只是索引树被扫描。这有两种方式:

如果索引是查询的覆盖索引,可以满足表中所有需要的数据,则只扫描索引树。在这种情况下,该Extra列显示 Using index。仅索引扫描通常比ALL索引的大小通常小于表数据的大小要快 。

使用从索引中读取来执行全表扫描以按索引顺序查找数据行。 Uses index不会出现在 Extra列中。

当查询仅使用属于单个索引的列时,MySQL 可以使用此连接类型。

ALL

对先前表中的每个行组合进行全表扫描。如果该表是第一个未标记的表 const,这通常不好,并且在所有其他情况下通常 非常糟糕。通常,您可以ALL通过添加索引来避免 基于常量值或早期表中的列值从表中检索行。

Author: suce
Link: https://haoubox.cn/2021/08/03/MySql之EXPLAN详解/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.