Explain工具
使用EXPLAIN关键字可以模拟优化器执行SQL语句,分析查询语句或是结构的性能瓶颈
在 select 语句之前增加 explain 关键字,MySQL 会在查询上设置一个标记,执行查询会返回执行计划的信息,而不是执行SQL
注意:如果 from 中包含子查询,仍会执行该子查询,将结果放入临时表中
官方文档:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
该EXPLAIN
语句提供有关 MySQL 如何执行语句的信息。 EXPLAIN
作品有 SELECT
, DELETE
, INSERT
, REPLACE
,和 UPDATE
语句。
EXPLAIN
为SELECT
语句中使用的每个表返回一行信息 。它按照 MySQL 在处理语句时读取它们的顺序列出输出中的表。MySQL 使用嵌套循环连接方法解析所有连接。这意味着 MySQL 从第一个表中读取一行,然后在第二个表中找到匹配的行,第三个表,依此类推。处理完所有表后,MySQL 输出选定的列并通过表列表回溯,直到找到具有更多匹配行的表。从此表中读取下一行,然后处理下一个表。
EXPLAIN
输出包括分区信息。此外,对于SELECT
语句,EXPLAIN
产生可与被显示扩展信息 SHOW WARNINGS
之后的 EXPLAIN
。
输出类信息
JSON 名称 | 意义 | |
---|---|---|
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 |
没有任何 | 附加信息 |
JSONNULL
格式的EXPLAIN
输出中未显示的JSON 属性。
id
(JSON名:select_id
)的
SELECT
标识符。这是SELECT
查询中的序列号 。NULL
如果该行引用其他行的联合结果,则该值可以是。在这种情况下,该table
列显示的值类似于 表示该行引用具有 和值的行的 并集 。<union*
M*,*
N*>``id
M
**N
select_type
(JSON 名称:无)的类型
SELECT
,可以是下表中的任何一种。JSON 格式EXPLAIN
将SELECT
类型公开 为 a 的属性query_block
,除非它是SIMPLE
或PRIMARY
。JSON 名称(如果适用)也显示在表中。|
select_type
价值 | JSON 名称 | 意义 |
| :—————————————————————————————- | :—————————————- | :—————————————————————————————- |
|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
,为外部上下文的每一行重新评估子查询。子查询的可缓存性不同于在查询缓存中缓存查询结果。子查询缓存发生在查询执行期间,而查询缓存仅用于在查询执行完成后存储结果。
当您指定
FORMAT=JSON
with 时EXPLAIN
,输出没有直接等效于select_type
; 的单个属性 。该query_block
属性对应于给定的SELECT
。与SELECT
刚刚显示的大多数子查询类型等效的属性可用(例如materialized_from_subquery
forMATERIALIZED
),并在适当时显示。SIMPLE
或没有 JSON 等价物PRIMARY
。select_type
非SELECT
语句 的值显示受影响表的语句类型。例如,select_type
isDELETE
forDELETE
语句。table
(JSON名:table_name
)输出行所引用的表的名称。这也可以是以下值之一:
<union*
M*,*
N*>
: 行是指具有 和id
值的行 的M
并集N
。<derived*
N*>
:该行是指用于与该行的派生表结果id
的值N
。例如,派生表可能来自FROM
子句中的子查询 。<subquery*
N*>
:该行是指与物化子查询该行的结果id
的值N
。请参阅 第 8.2.2.2 节,“使用实现优化子查询”。
partitions
(JSON名:partitions
)查询将匹配记录的分区。该值
NULL
用于非分区表。请参阅 第 22.3.5 节,“获取有关分区的信息”。type
(JSON名:access_type
)联接类型。有关不同类型的说明,请参阅
EXPLAIN
联接类型。possible_keys
(JSON名:possible_keys
)该
possible_keys
列指示 MySQL 可以选择从中查找该表中行的索引。请注意,此列完全独立于从 的输出中显示的表的顺序EXPLAIN
。这意味着某些键possible_keys
在实际中可能无法与生成的表顺序一起使用。如果此列是
NULL
(或在 JSON 格式的输出中未定义),则没有相关索引。在这种情况下,您可以通过检查WHERE
子句来检查它是否引用了适合编制索引的某些列或多列,从而提高查询的性能。如果是这样,请创建适当的索引并EXPLAIN
再次检查查询 。见 第 13.1.8 节,“ALTER TABLE 语句”。要查看表具有哪些索引,请使用.
SHOW INDEX FROM *
tbl_name*
key
(JSON名: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名:key_length
)该
key_len
列表示 MySQL 决定使用的键的长度。的值key_len
使您能够确定 MySQL 实际使用的多部分键的多少部分。如果key
列说NULL
,key_len
列也说NULL
。由于密钥存储格式的原因,列的密钥长度
NULL
比列的长度NOT NULL
大一。ref
(JSON名:ref
)该
ref
列显示哪些列或常量与列中指定的索引进行比较以key
从表中选择行。如果值为
func
,则使用的值是某个函数的结果。要查看哪个功能,请使用SHOW WARNINGS
以下内容EXPLAIN
查看扩展EXPLAIN
输出。该函数实际上可能是一个运算符,例如算术运算符。rows
(JSON名:rows
)该
rows
列表示 MySQL 认为它必须检查以执行查询的行数。对于
InnoDB
表格,这个数字是一个估计值,可能并不总是准确的。filtered
(JSON名:filtered
)该
filtered
列指示按表条件过滤的表行的估计百分比。最大值为 100,这意味着没有发生行过滤。从 100 开始减小的值表示过滤量增加。rows
显示检查的估计行数,rows
×filtered
显示与下表连接的行数。例如,如果rows
是 1000 并且filtered
是 50.00 (50%),那么与下表连接的行数为 1000 × 50% = 500。Extra
(JSON 名称:无)此列包含有关 MySQL 如何解析查询的附加信息。有关不同值的说明,请参阅
EXPLAIN
额外信息。没有与
Extra
列对应的单个 JSON 属性 ;但是,此列中可能出现的值会作为 JSON 属性或作为属性的文本公开message
。
联接类型
该type
列 EXPLAIN
输出介绍如何联接表。在 JSON 格式的输出中,这些作为access_type
属性的值被找到。下面的列表描述了连接类型,从最好的类型到最差的类型:
-
该表只有一行(= 系统表)。这是
const
连接类型的一个特例 。 -
该表最多有一个匹配行,在查询开始时读取。因为只有一行,该行中该列的值可以被优化器的其余部分视为常量。
const
表非常快,因为它们只被读取一次。const
当您将 aPRIMARY KEY
或UNIQUE
索引的所有部分与常量值进行比较时使用。在以下查询中,tbl_name
可以用作const
表:1
2
3
4SELECT * FROM tbl_name WHERE primary_key=1;
SELECT * FROM tbl_name
WHERE primary_key_part1=1 AND primary_key_part2=2; -
对于先前表中的每个行组合,从该表中读取一行。除了
system
andconst
类型之外,这是最好的连接类型。当连接使用索引的所有部分并且索引是一个PRIMARY KEY
或UNIQUE NOT NULL
索引时使用它。eq_ref
可用于使用=
运算符进行比较的索引列 。比较值可以是常量或表达式,该表达式使用在此表之前读取的表中的列。在以下示例中,MySQL 可以使用eq_ref
连接来处理ref_table
:1
2
3
4
5
6SELECT * 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
如果联接仅使用键的最左前缀或键不是 aPRIMARY KEY
或UNIQUE
索引(换句话说,如果联接无法根据键值选择单行),则使用。如果使用的键只匹配几行,这是一个很好的连接类型。ref
可用于使用=
or<=>
运算符进行比较的索引列 。在以下示例中,MySQL 可以使用ref
连接来处理ref_table
:1
2
3
4
5
6
7
8SELECT * 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
索引执行的。 -
这种连接类型类似于
ref
,但另外,MySQL 会额外搜索包含NULL
值的行。这种连接类型优化最常用于解析子查询。在以下示例中,MySQL 可以使用ref_or_null
连接来处理ref_table
:1
2SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL; index_merge
此连接类型表示使用了索引合并优化。在这种情况下,key
输出行中的列包含所使用索引的列表,并key_len
包含所使用索引 的最长关键部分的列表。有关更多信息,请参阅 第 8.2.1.3 节,“索引合并优化”。-
这种类型替代 了以下形式的
eq_ref
一些IN
子查询:1
value IN (SELECT primary_key FROM single_table WHERE some_expr)
unique_subquery
只是一个索引查找函数,完全替换子查询以提高效率。 -
这种联接类型类似于
unique_subquery
. 它取代了IN
子查询,但它适用于以下形式的子查询中的非唯一索引:1
value IN (SELECT key_column FROM single_table WHERE some_expr)
-
仅检索给定范围内的行,使用索引来选择行。的
key
输出行中的列指示使用哪个索引。将key_len
包含已使用的时间最长的关键部分。该ref
列适用NULL
于这种类型。range
当使用=
,<>
,>
,>=
,<
,<=
,IS NULL
,<=>
,BETWEEN
,LIKE
, 或IN()
运算符中的任何一个将键列与常量进行比较时,可以使用 :1
2
3
4
5
6
7
8
9
10
11SELECT * 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
联接类型是一样的ALL
,只是索引树被扫描。这有两种方式:- 如果索引是查询的覆盖索引,可以满足表中所有需要的数据,则只扫描索引树。在这种情况下,该
Extra
列显示Using index
。仅索引扫描通常比ALL
索引的大小通常小于表数据的大小要快 。 - 使用从索引中读取来执行全表扫描以按索引顺序查找数据行。
Uses index
不会出现在Extra
列中。
当查询仅使用属于单个索引的列时,MySQL 可以使用此连接类型。
- 如果索引是查询的覆盖索引,可以满足表中所有需要的数据,则只扫描索引树。在这种情况下,该
-
对先前表中的每个行组合进行全表扫描。如果表是第一个未标记的表
const
,这通常不好,并且在所有其他情况下通常 非常糟糕。通常,您可以ALL
通过添加索引来避免 基于常量值或早期表中的列值从表中检索行。
额外信息
该Extra
列 EXPLAIN
输出包含MySQL解决查询的额外信息。以下列表说明了可以出现在此列中的值。每个项目还为 JSON 格式的输出指示哪个属性显示Extra
值。对于其中一些,有一个特定的属性。其他显示为message
属性的文本。
如果您想尽可能快地进行查询,请注意and 的Extra
列值,或者,在 JSON 格式的输出中, for 和 properties 等于 。 Using filesort``Using temporary``EXPLAIN``using_filesort``using_temporary_table``true
Child of '*
table*' pushed join@1
(JSON:message
文本)此表被引用为
table
可以下推到 NDB 内核的连接中的子项。仅适用于 NDB Cluster,当启用下推连接时。有关ndb_join_pushdown
更多信息和示例,请参阅服务器系统变量的描述 。const row not found
(JSON属性:const_row_not_found
)对于诸如 之类的查询,该表为空。
SELECT ... FROM *
tbl_name*
Deleting all rows
(JSON属性:message
)对于
DELETE
,某些存储引擎(例如MyISAM
)支持以简单快速的方式删除所有表行的处理程序方法。Extra
如果引擎使用此优化,则会显示此值。Distinct
(JSON属性:distinct
)MySQL 正在寻找不同的值,因此它在找到第一个匹配行后停止为当前行组合搜索更多行。
FirstMatch(*
tbl_name*)
(JSON属性:first_match
)半连接 FirstMatch 连接快捷策略用于
tbl_name
.Full scan on NULL key
(JSON属性:message
)当优化器无法使用索引查找访问方法时,子查询优化作为回退策略会发生这种情况。
Impossible HAVING
(JSON属性:message
)该
HAVING
子句始终为 false,不能选择任何行。Impossible WHERE
(JSON属性:message
)该
WHERE
子句始终为 false,不能选择任何行。Impossible WHERE noticed after reading const tables
(JSON属性:message
)LooseScan(*
m*..*
n*)
(JSON属性:message
)使用了半连接 LooseScan 策略。
m
和n
是关键部件号。No matching min/max row
(JSON属性:message
)没有行满足查询条件,例如 。
SELECT MIN(...) FROM ... WHERE *
condition*
no matching row in const table
(JSON属性:message
)对于带有连接的查询,有一个空表或一个没有满足唯一索引条件的行的表。
No matching rows after partition pruning
(JSON属性:message
)对于
DELETE
orUPDATE
,优化器在分区修剪后没有发现要删除或更新的内容。它的含义类似于Impossible WHERE
forSELECT
语句。No tables used
(JSON属性:message
)查询没有
FROM
子句,或有FROM DUAL
子句。对于
INSERT
orREPLACE
语句,EXPLAIN
当没有SELECT
部件时显示该值。例如,它出现是EXPLAIN INSERT INTO t VALUES(10)
因为它等价于EXPLAIN INSERT INTO t SELECT 10 FROM DUAL
。Not exists
(JSON属性:message
)MySQL 能够对
LEFT JOIN
查询进行优化,并且在找到符合LEFT JOIN
条件的行后,不会检查该表中前一行组合的更多行。以下是可以通过这种方式优化的查询类型的示例:1
2SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id
WHERE t2.id IS NULL;假设
t2.id
定义为NOT NULL
。在这种情况下,MySQL使用 的值 扫描t1
和查找行 。如果 MySQL 在 中找到匹配的行 ,它知道 永远不可能是 ,并且不会扫描具有相同值的其余行。换句话说,对于 中的每一行,MySQL 只需要在 中进行一次查找,而不管 中实际匹配了多少行。t2``t1.id``t2``t2.id``NULL``t2``id``t1``t2``t2
Plan isn't ready yet
(JSON 属性:无)EXPLAIN FOR CONNECTION
当优化器尚未完成为在命名连接中执行的语句创建执行计划时, 会出现此值。如果执行计划输出包含多行,则其中任何一行或所有行都可以具有此Extra
值,具体取决于优化器确定完整执行计划的进度。Range checked for each record (index map: *
N*)
(JSON属性:message
)MySQL 没有发现可以使用的好的索引,但是发现某些索引可能会在已知前表中的列值后使用。对于前面表中的每个行组合,MySQL 会检查是否可以使用 a
range
或index_merge
access 方法来检索行。这不是很快,但比执行完全没有索引的连接要快。适用性标准如 第 8.2.1.2 节“范围优化”和 第 8.2.1.3 节“索引合并优化”所述,除了上表的所有列值都是已知的并被视为常量。索引从 1 开始编号,其顺序
SHOW INDEX
与表中 的 相同。索引映射值N
是指示哪些索引是候选的位掩码值。例如,值0x19
(binary 11001) 表示考虑索引 1、4 和 5。Scanned *
N* databases
(JSON属性:message
)这表示服务器在处理
INFORMATION_SCHEMA
表查询时执行的目录扫描次数 ,如第 8.2.3 节“优化 INFORMATION_SCHEMA 查询”中所述。的值N
可以是 0、1 或all
。Select tables optimized away
(JSON属性:message
)优化器确定 1) 最多应返回一行,以及 2) 要生成该行,必须读取一组确定性的行。当在优化阶段可以读取要读取的行(例如通过读取索引行)时,查询执行期间不需要读取任何表。
当查询被隐式分组(包含聚合函数但没有
GROUP BY
子句)时,满足第一个条件 。当对每个使用的索引执行一次行查找时,满足第二个条件。读取的索引数决定了要读取的行数。考虑以下隐式分组查询:
1
SELECT MIN(c1), MIN(c2) FROM t1;
假设
MIN(c1)
可以通过读取一个索引行MIN(c2)
来检索,并且可以通过从不同的索引读取一行来检索。也就是说,对于每一列c1
andc2
,都存在一个索引,其中该列是索引的第一列。在这种情况下,通过读取两个确定性行返回一行。Extra
如果要读取的行不确定,则不会出现 此值。考虑这个查询:1
SELECT MIN(c2) FROM t1 WHERE c1 <= 10;
假设这
(c1, c2)
是一个覆盖索引。使用此索引,c1 <= 10
必须扫描所有行以找到最小值c2
。相比之下,请考虑以下查询:1
SELECT MIN(c2) FROM t1 WHERE c1 = 10;
在这种情况下,第一个索引行
c1 = 10
包含最小值c2
。只需读取一行即可生成返回的行。对于为每个表维护精确行数的存储引擎(例如
MyISAM
,但不是InnoDB
),对于缺少子句或始终为真且没有 子句的查询,Extra
可能会出现此值。(这是一个隐式分组查询的实例,其中存储引擎会影响是否可以读取确定数量的行。)COUNT(*)``WHERE``GROUP BY
Skip_open_table
,Open_frm_only
,Open_full_table
(JSON属性:message
)这些值表示适用于
INFORMATION_SCHEMA
表查询的文件打开优化,如 第 8.2.3 节“优化 INFORMATION_SCHEMA 查询”中所述。Skip_open_table
: 表文件不需要打开。通过扫描数据库目录,该信息已在查询中可用。Open_frm_only
: 只.frm
需要打开表的文件。Open_full_table
:未优化的信息查找。的.frm
,.MYD
和.MYI
文件必须被打开。
Start temporary
,End temporary
(JSON属性:message
)这表示半连接重复清除策略的临时表使用。
unique row not found
(JSON属性:message
)对于诸如 的查询,没有行满足 索引或表的条件。
SELECT ... FROM *
tbl_name*``UNIQUE``PRIMARY KEY
Using filesort
(JSON属性:using_filesort
)MySQL 必须执行额外的检查以找出如何按排序顺序检索行。排序是通过根据连接类型遍历所有行并存储排序键和指向与
WHERE
子句匹配的所有行的行的指针来完成的。然后对键进行排序,并按排序顺序检索行。请参见 第 8.2.1.14 节,“ORDER BY 优化”。Using index
(JSON属性:using_index
)仅使用索引树中的信息从表中检索列信息,而无需执行额外的查找来读取实际行。当查询仅使用属于单个索引的列时,可以使用此策略。
对于
InnoDB
具有用户定义的聚集索引的表,即使列中Using index
不存在 该索引,也可以使用该索引Extra
。如果type
isindex
和key
is就是这种情况PRIMARY
。Using index condition
(JSON属性:using_index_condition
)通过访问索引元组并首先测试它们以确定是否读取完整的表行来读取表。通过这种方式,索引信息用于推迟(“下推”)读取全表行,除非有必要。请参见 第 8.2.1.5 节,“索引条件下推优化”。
Using index for group-by
(JSON属性:using_index_for_group_by
)与
Using index
表访问方法类似,Using index for group-by
表示 MySQL 找到了一个索引,该索引可用于检索 aGROUP BY
或DISTINCT
查询的所有列,而无需对实际表进行任何额外的磁盘访问。此外,索引以最有效的方式使用,因此对于每个组,仅读取少数索引条目。有关详细信息,请参阅 第 8.2.1.15 节,“GROUP BY 优化”。Using join buffer (Block Nested Loop)
,Using join buffer (Batched Key Access)
(JSON属性:using_join_buffer
)来自早期连接的表被分部分读入连接缓冲区,然后它们的行从缓冲区中用于执行与当前表的连接。
(Block Nested Loop)
指示使用块嵌套循环算法并(Batched Key Access)
指示使用批处理密钥访问算法。也就是说,EXPLAIN
输出前一行表中的键 被缓冲,匹配的行从Using join buffer
出现的行表示的表中批量获取 。在 JSON 格式的输出中, 的值
using_join_buffer
始终是Block Nested Loop
或 之一Batched Key Access
。有关这些算法的更多信息,请参阅 Block Nested-Loop Join Algorithm和 Batched Key Access Joins。
Using MRR
(JSON属性:message
)使用多范围读取优化策略读取表。请参见第 8.2.1.10 节,“多范围读取优化”。
Using sort_union(...)
,Using union(...)
,Using intersect(...)
(JSON属性:message
)这些指示显示如何为
index_merge
连接类型合并索引扫描的特定算法 。请参阅第 8.2.1.3 节,“索引合并优化”。Using temporary
(JSON属性:using_temporary_table
)为了解决查询,MySQL 需要创建一个临时表来保存结果。如果查询包含以不同方式列出列的
GROUP BY
和ORDER BY
子句,则通常会发生这种情况。Using where
(JSON属性:attached_condition
)甲
WHERE
子句用于限制来匹配下一个表或发送到客户端的行。除非您特别打算从表中获取或检查所有行,否则如果该Extra
值不是Using where
并且表连接类型是ALL
or ,则您的查询可能会出错index
。Using where
在 JSON 格式的输出中没有直接对应物;该attached_condition
属性包含使用的任何WHERE
条件。Using where with pushed condition
(JSON属性:message
)此产品适用于
NDB
表只。这意味着 NDB Cluster 正在使用 Condition Pushdown 优化来提高非索引列和常量之间直接比较的效率。在这种情况下,条件被“下推”到集群的数据节点,并同时在所有数据节点上进行评估。这消除了通过网络发送不匹配行的需要,并且可以将此类查询的速度提高 5 到 10 倍,比可以使用但未使用条件下推的情况。有关更多信息,请参阅 第 8.2.1.4 节,“发动机状态下推优化”。Zero limit
(JSON属性:message
)查询有一个
LIMIT 0
子句,不能选择任何行。
EXPLAIN 输出解释
通过取输出rows
列中的值的乘积,您可以很好地表明连接的好坏EXPLAIN
。这应该告诉您 MySQL 必须检查多少行才能执行查询。如果您使用max_join_size
系统变量限制查询,则 此行积还用于确定SELECT
要执行哪些多表语句以及要中止哪些多表语句。
以下示例显示了如何根据 提供的信息逐步优化多表连接 EXPLAIN
。
假设您有SELECT
此处显示的 语句,并且您计划使用EXPLAIN
以下命令检查它 :
1 | EXPLAIN SELECT tt.TicketNumber, tt.TimeIn, |
对于此示例,请做出以下假设:
被比较的列已声明如下。
| 桌子 | 柱子 | 数据类型 |
| :—- | :—————- | :————- |
|tt
|ActualPC
|CHAR(10)
|
|tt
|AssignedPC
|CHAR(10)
|
|tt
|ClientID
|CHAR(10)
|
|et
|EMPLOYID
|CHAR(15)
|
|do
|CUSTNMBR
|CHAR(15)
|这些表具有以下索引。
| 桌子 | 指数 |
| :—- | :———————————— |
|tt
|ActualPC
|
|tt
|AssignedPC
|
|tt
|ClientID
|
|et
|EMPLOYID
(首要的关键) |
|do
|CUSTNMBR
(首要的关键) |这些
tt.ActualPC
值不是均匀分布的。
最初,在执行任何优化之前,该 EXPLAIN
语句会生成以下信息:
1 | table type possible_keys key key_len ref rows Extra |
因为type
是 ALL
为每个表,这个输出表明MySQL正在生成的所有表的笛卡儿积; 也就是说,行的每个组合。这需要相当长的时间,因为必须检查每个表中行数的乘积。对于手头的情况,该乘积为 74 × 2135 × 74 × 3872 = 45,268,558,720 行。如果桌子更大,您只能想象需要多长时间。
这里的一个问题是,如果将列声明为相同的类型和大小,则 MySQL 可以更有效地使用列上的索引。在这种情况下,VARCHAR
与 CHAR
被认为是相同的,如果它们被声明为相同的大小。 tt.ActualPC
被声明为 CHAR(10)
and et.EMPLOYID
is CHAR(15)
,因此存在长度不匹配。
要修复列长度之间的这种差异,请使用 ALTER TABLE
将ActualPC
10 个字符延长 到 15 个字符:
1 | mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15); |
现在tt.ActualPC
和 et.EMPLOYID
都是 VARCHAR(15)
。EXPLAIN
再次执行该 语句会产生以下结果:
1 | table type possible_keys key key_len ref rows Extra |
这并不完美,但要好得多:rows
值的乘积 少了 74 倍。此版本在几秒钟内执行。
可以进行第二次更改以消除tt.AssignedPC = et_1.EMPLOYID
和tt.ClientID = do.CUSTNMBR
比较的列长度不匹配:
1 | mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15), |
修改后, EXPLAIN
生成此处显示的输出:
1 | table type possible_keys key key_len ref rows Extra |
在这一点上,查询几乎被优化了。剩下的问题是,默认情况下,MySQL 假设tt.ActualPC
列中的值是均匀分布的,而tt
表并非如此。幸运的是,很容易告诉 MySQL 分析密钥分布:
1 | mysql> ANALYZE TABLE tt; |
使用额外的索引信息,连接是完美的并 EXPLAIN
产生以下结果:
1 | table type possible_keys key key_len ref rows Extra |
rows
输出中 的列 EXPLAIN
是来自 MySQL 连接优化器的有根据的猜测。通过将rows
乘积与查询返回的实际行数进行比较,检查数字是否更接近真实 情况。如果数字完全不同,您可能会通过STRAIGHT_JOIN
在 SELECT
语句中使用并尝试在FROM
子句中以不同顺序列出表来 获得更好的性能 。(但是, STRAIGHT_JOIN
可能会阻止使用索引,因为它禁用了半连接转换。请参阅第 8.2.2.1 节,“使用半连接转换 优化子查询、派生表和视图引用”.)
在某些情况下,可以在EXPLAIN SELECT
与子查询一起使用时执行修改数据的语句;有关更多信息,请参阅第 13.2.10.8 节,“派生表”。
...
...
Copyright 2021 sunfy.top ALL Rights Reserved