文档首页
MySQL 8.4 参考手册
相关文档 下载本手册
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
手册页 (TGZ) - 258.5Kb
手册页 (Zip) - 365.5Kb
信息 (Gzip) - 4.0Mb
信息 (Zip) - 4.0Mb


MySQL 8.4 参考手册  /  ...  /  EXPLAIN 输出格式

10.8.2 EXPLAIN 输出格式

EXPLAIN 语句提供有关 MySQL 如何执行语句的信息。EXPLAIN 适用于 SELECTDELETEINSERTREPLACEUPDATE 语句。

EXPLAINSELECT 语句中使用的每个表返回一行信息。它在输出中按 MySQL 处理语句时读取表的顺序列出表。这意味着 MySQL 从第一个表中读取一行,然后在第二个表中找到匹配的行,然后在第三个表中找到匹配的行,依此类推。当处理完所有表后,MySQL 输出选定的列并回溯表列表,直到找到一个还有更多匹配行的表。从该表中读取下一行,并继续处理下一个表。

注意

MySQL Workbench 具有可视化解释功能,可以直观地表示 EXPLAIN 输出。请参阅 教程:使用 Explain 提高查询性能

EXPLAIN 输出列

本节介绍 EXPLAIN 生成的输出列。后面的章节将提供有关 typeExtra 列的更多信息。

EXPLAIN 的每一行输出都提供有关一个表的信息。每行都包含 表 10.1,“EXPLAIN 输出列” 中汇总的值,并在该表之后进行更详细的描述。列名显示在表的第一列;第二列提供了使用 FORMAT=JSON 时输出中显示的等效属性名称。

表 10.1 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 附加信息

注意

JSON 格式的 EXPLAIN 输出中不会显示 NULL 的 JSON 属性。

  • id(JSON 名称:select_id

    SELECT 标识符。这是查询中 SELECT 的序号。如果该行引用其他行的并集结果,则该值可以是 NULL。在这种情况下,table 列会显示类似 <unionM,N> 的值,以指示该行引用 id 值为 MN 的行的并集。

  • select_type(JSON 名称:无)

    SELECT 的类型,可以是下表中显示的任何一种。JSON 格式的 EXPLAIN 会将 SELECT 类型作为 query_block 的一个属性公开,除非它是 SIMPLEPRIMARY。表中还显示了 JSON 名称(如果适用)。

    select_type JSON 名称 含义
    SIMPLE 简单的 SELECT(不使用 UNION 或子查询)
    PRIMARY 最外层的 SELECT
    UNION UNION 中的第二个或后续 SELECT 语句
    DEPENDENT UNION dependent (true) UNION 中的第二个或后续 SELECT 语句,依赖于外部查询
    UNION RESULT union_result UNION 的结果。
    SUBQUERY 子查询中的第一个 SELECT
    DEPENDENT SUBQUERY dependent (true) 子查询中的第一个 SELECT,依赖于外部查询
    DERIVED 派生表
    DEPENDENT DERIVED dependent (true) 依赖于另一个表的派生表
    MATERIALIZED materialized_from_subquery 物化子查询
    UNCACHEABLE SUBQUERY cacheable (false) 一个子查询,其结果无法缓存,并且必须针对外部查询的每一行重新评估
    UNCACHEABLE UNION cacheable (false) UNION 中的第二个或后续 select,属于不可缓存的子查询(请参阅 UNCACHEABLE SUBQUERY

    DEPENDENT 通常表示使用相关子查询。请参阅 第 15.2.15.7 节“相关子查询”

    DEPENDENT SUBQUERY 的评估与 UNCACHEABLE SUBQUERY 的评估不同。对于 DEPENDENT SUBQUERY,子查询仅针对其外部上下文中变量的每组不同值重新评估一次。对于 UNCACHEABLE SUBQUERY,子查询针对外部上下文的每一行重新评估。

    当您在 EXPLAIN 中指定 FORMAT=JSON 时,输出没有与 select_type 直接等效的单个属性;query_block 属性对应于给定的 SELECT。与大多数刚刚显示的 SELECT 子查询类型等效的属性可用(例如,MATERIALIZEDmaterialized_from_subquery),并在适当时显示。没有与 SIMPLEPRIMARY 等效的 JSON。

    SELECT 语句的 select_type 值显示受影响表的语句类型。例如,对于 DELETE 语句,select_typeDELETE

  • table(JSON 名称:table_name

    输出行所指表的名称。这也可以是以下值之一

    • <unionM,N>:该行指的是 id 值为 MN 的行的并集。

    • <derivedN>:该行指的是 id 值为 N 的行的派生表结果。例如,派生表可能是 FROM 子句中的子查询的结果。

    • <subqueryN>:该行指的是 id 值为 N 的行的物化子查询的结果。请参阅 第 10.2.2.2 节“使用物化优化子查询”

  • partitions(JSON 名称:partitions

    查询将从中匹配记录的分区。对于非分区表,该值为 NULL。请参阅 第 26.3.5 节“获取有关分区的信息”

  • type(JSON 名称:access_type

    连接类型。有关不同类型的描述,请参阅 EXPLAIN 连接类型

  • possible_keys(JSON 名称:possible_keys

    possible_keys 列指示 MySQL 可以从中选择以查找此表中的行的索引。请注意,此列完全独立于 EXPLAIN 输出中显示的表顺序。这意味着 possible_keys 中的某些键在实践中可能无法与生成的表顺序一起使用。

    如果此列为 NULL(或在 JSON 格式的输出中未定义),则表示没有相关的索引。在这种情况下,您可以通过检查 WHERE 子句以查看它是否引用了适合索引的某些列来提高查询的性能。如果是这样,请创建一个适当的索引,并使用 EXPLAIN 再次检查查询。请参阅 第 15.1.9 节“ALTER TABLE 语句”

    要查看表有哪些索引,请使用 SHOW INDEX FROM tbl_name

  • key(JSON 名称:key

    key 列指示 MySQL 实际决定使用的键(索引)。如果 MySQL 决定使用 possible_keys 索引之一来查找行,则该索引将列为键值。

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

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

    要强制 MySQL 使用或忽略 possible_keys 列中列出的索引,请在查询中使用 FORCE INDEXUSE INDEXIGNORE INDEX。请参阅 第 10.9.4 节“索引提示”

    对于 MyISAM 表,运行 ANALYZE TABLE 有助于优化器选择更好的索引。对于 MyISAM 表,myisamchk --analyze 具有相同的作用。请参阅 第 15.7.3.1 节“ANALYZE TABLE 语句”第 9.6 节“MyISAM 表维护和崩溃恢复”

  • key_len(JSON 名称:key_length

    key_len 列指示 MySQL 决定使用的键的长度。key_len 的值使您能够确定 MySQL 实际使用了多部分键的多少部分。如果 key 列显示 NULL,则 key_len 列也显示 NULL

    由于键的存储格式,对于可以为 NULL 的列,键的长度比 NOT NULL 列的键长度大 1。

  • ref(JSON 名称:ref

    ref 列显示哪些列或常量与 key 列中命名的索引进行比较,以从表中选择行。

    如果值为 func,则使用的值是某些函数的结果。要查看是哪个函数,请在 EXPLAIN 之后使用 SHOW WARNINGS 来查看扩展的 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 属性的文本公开。

EXPLAIN 连接类型

EXPLAIN 输出的 type 列描述了表的连接方式。在 JSON 格式的输出中,这些值作为 access_type 属性的值找到。以下列表描述了连接类型,从最佳类型到最差类型排序

  • system

    该表只有一行(= 系统表)。这是 const 连接类型的一种特殊情况。

  • const

    该表最多有一个匹配行,该行在查询开始时读取。因为只有一行,所以此行中列的值可以被优化器的其余部分视为常量。const 表非常快,因为它们只读取一次。

    当您将 PRIMARY KEYUNIQUE 索引的所有部分与常量值进行比较时,将使用 const。在以下查询中,tbl_name 可以用作 const

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

    对于先前表中每行的组合,将从此表中读取一行。除了 systemconst 类型之外,这是最佳连接类型。当索引的所有部分都被连接使用并且索引是 PRIMARY KEYUNIQUE NOT NULL 索引时,将使用它。

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

    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

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

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

    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

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

    请参阅 第 10.2.1.15 节“IS NULL 优化”

  • index_merge

    此连接类型表示使用了索引合并优化。在这种情况下,输出行中的 key 列包含使用的索引列表,key_len 包含使用的索引的最长键部分列表。有关更多信息,请参阅 第 10.2.1.3 节“索引合并优化”

  • unique_subquery

    此类型替换了以下形式的某些 IN 子查询的 eq_ref

    value IN (SELECT primary_key FROM single_table WHERE some_expr)

    unique_subquery 只是一个索引查找函数,它完全替换了子查询以提高效率。

  • index_subquery

    此连接类型类似于 unique_subquery。它替换了 IN 子查询,但它适用于以下形式的子查询中的非唯一索引

    value IN (SELECT key_column FROM single_table WHERE some_expr)
  • range

    仅检索给定范围内的行,使用索引来选择行。输出行中的 key 列指示使用了哪个索引。key_len 包含使用的最长键部分。ref 列对于此类型为 NULL

    当使用 =<>>>=<<=IS NULL<=>BETWEENLIKEIN() 运算符将键列与常量进行比较时,可以使用 range

    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,这些索引允许根据常量值或先前表中的列值从表中检索行。

EXPLAIN 额外信息

EXPLAIN 输出的 Extra 列包含有关 MySQL 如何解析查询的附加信息。以下列表说明了此列中可能出现的值。每个项目还指示了 JSON 格式的输出,哪个属性显示 Extra 值。对于其中一些,有一个特定的属性。其他显示为 message 属性的文本。

如果要使查询尽可能快,请注意 Extra 列值为 Using filesortUsing temporary,或者在 JSON 格式的 EXPLAIN 输出中,using_filesortusing_temporary_table 属性等于 true

  • Backward index scan(JSON:backward_index_scan

    优化器能够在 InnoDB 表上使用降序索引。与 Using index 一起显示。有关更多信息,请参阅 第 10.3.13 节“降序索引”

  • Child of 'table' pushed join@1(JSON:message 文本)

    此表在可以下推到 NDB 内核的连接中被引用为 table 的子级。仅适用于 NDB 集群,当启用下推连接时。有关更多信息和示例,请参阅 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 子句始终为假,并且无法选择任何行。

  • Impossible WHERE(JSON 属性:message

    WHERE 子句始终为假,并且无法选择任何行。

  • Impossible WHERE noticed after reading const tables(JSON 属性:message

    MySQL 已读取所有 const(和 system)表,并注意到 WHERE 子句始终为假。

  • LooseScan(m..n)(JSON 属性:message

    使用半连接 LooseScan 策略。mn 是键部分号。

  • 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

    对于 DELETEUPDATE,优化器在分区修剪后发现没有任何要删除或更新的内容。它的含义类似于 SELECT 语句的 Impossible WHERE

  • No tables used(JSON 属性:message

    该查询没有 FROM 子句,或者有一个 FROM DUAL 子句。

    对于 INSERTREPLACE 语句,当没有 SELECT 部分时,EXPLAIN 会显示此值。例如,它会出现在 EXPLAIN INSERT INTO t VALUES(10) 中,因为它等同于 EXPLAIN INSERT INTO t SELECT 10 FROM DUAL

  • Not exists(JSON 属性:message

    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 中的每一行,无论 t2 中实际匹配多少行,MySQL 只需要在 t2 中执行一次查找。

    这还可以表明,WHERE 条件形式为 NOT IN (子查询)NOT EXISTS (子查询) 已在内部转换为反连接。这将删除子查询并将其表引入最顶层查询的计划中,从而改进成本计划。通过合并半连接和反连接,优化器可以更自由地重新排序执行计划中的表,在某些情况下可以生成更快的计划。

    您可以通过检查执行 EXPLAINSHOW WARNINGS 中的 Message 列或 EXPLAIN FORMAT=TREE 的输出,来查看何时对给定查询执行反连接转换。

    注意

    反连接是半连接 表_a JOIN 表_b ON 条件 的补充。反连接返回 表_a 中所有在 表_b没有匹配 条件 的行的行。

  • 计划尚未准备好(JSON 属性:无)

    当优化器尚未完成为在命名连接中执行的语句创建执行计划时,此值将与 EXPLAIN FOR CONNECTION 一起出现。如果执行计划输出包含多行,则根据优化器确定完整执行计划的进度,其中任何一行或所有行都可能具有此 Extra 值。

  • 已为每条记录检查范围(索引映射:N(JSON 属性:message

    MySQL 没有找到要使用的良好索引,但发现知道前面表中的列值后,可能会使用某些索引。对于前面表中的每个行组合,MySQL 都会检查是否可以使用 rangeindex_merge 访问方法来检索行。这并不是很快,但比完全不使用索引执行连接要快。适用性标准如 第 10.2.1.2 节“范围优化”第 10.2.1.3 节“索引合并优化”中所述,但前面表的所有列值都是已知的并被视为常量。

    索引从 1 开始编号,顺序与 SHOW INDEX 为表显示的顺序相同。索引映射值 N 是一个位掩码值,指示哪些索引是候选索引。例如,值 0x19(二进制 11001)表示索引 1、4 和 5 被考虑。

  • 递归(JSON 属性:recursive

    这表示该行适用于递归 SELECT 部分的递归公用表表达式。请参阅 第 15.2.20 节“WITH(公用表表达式)”

  • 重新物化(JSON 属性:rematerialize

    重新物化 (X,...) 显示在表 TEXPLAIN 行中,其中 X 是任何在读取 T 的新行时触发其重新物化的横向派生表。例如

    SELECT
      ...
    FROM
      t,
      LATERAL (derived table that refers to t) AS dt
    ...

    每次顶级查询处理 t 的新行时,都会重新物化派生表的内容以使其保持最新。

  • 已扫描 N 个数据库(JSON 属性:message

    这指示服务器在处理 INFORMATION_SCHEMA 表的查询时执行了多少次目录扫描,如 第 10.2.3 节“优化 INFORMATION_SCHEMA 查询” 中所述。N 的值可以是 0、1 或 all

  • 选择表已优化掉(JSON 属性:message

    优化器确定 1) 最多应返回一行,以及 2) 要生成此行,必须读取一组确定性行。当可以在优化阶段读取要读取的行时(例如,通过读取索引行),则在查询执行期间无需读取任何表。

    当查询被隐式分组(包含聚合函数但没有 GROUP BY 子句)时,将满足第一个条件。当每个使用的索引执行一行查找时,将满足第二个条件。读取的索引数决定了要读取的行数。

    请考虑以下隐式分组查询

    SELECT MIN(c1), MIN(c2) FROM t1;

    假设可以通过读取一行索引行来检索 MIN(c1),并且可以通过从不同索引读取一行来检索 MIN(c2)。也就是说,对于每列 c1c2,都存在一个索引,其中该列是索引的第一列。在这种情况下,将返回一行,该行是通过读取两行确定性行生成的。

    如果要读取的行不是确定性的,则不会出现此 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 值。只需读取一行即可生成返回的行。

    对于每个表维护精确行数的存储引擎(例如 MyISAM,但不是 InnoDB),此 Extra 值可能会出现在 COUNT(*) 查询中,其中 WHERE 子句丢失或始终为 true 并且没有 GROUP BY 子句。(这是隐式分组查询的一个实例,其中存储引擎会影响是否可以读取确定数量的行。)

  • Skip_open_tableOpen_frm_onlyOpen_full_table(JSON 属性:message

    这些值指示适用于 INFORMATION_SCHEMA 表的查询的文件打开优化。

    • Skip_open_table:不需要打开表文件。该信息已从数据字典中获得。

    • Open_frm_only:只需读取数据字典以获取表信息。

    • Open_full_table:未优化的信息查找。必须从数据字典和读取表文件来读取表信息。

  • Start temporaryEnd temporary(JSON 属性:message

    这表示半连接重复数据消除策略使用临时表。

  • unique row not found(JSON 属性:message

    对于 SELECT ... FROM tbl_name 等查询,没有行满足表上的 UNIQUE 索引或 PRIMARY KEY 的条件。

  • Using filesort(JSON 属性:using_filesort

    MySQL 必须执行额外的遍历才能找出如何按排序顺序检索行。排序是通过根据连接类型遍历所有行并存储与 WHERE 子句匹配的所有行的排序关键字和指向该行的指针来完成的。然后对关键字进行排序,并按排序顺序检索行。请参阅 第 10.2.1.16 节“ORDER BY 优化”

  • Using index(JSON 属性:using_index

    仅使用索引树中的信息即可从表中检索列信息,而无需执行额外的查找来读取实际行。当查询仅使用属于单个索引的列时,可以使用此策略。

    对于具有用户定义的聚集索引的 InnoDB 表,即使 Using index 不在 Extra 列中,也可以使用该索引。如果 typeindex 并且 keyPRIMARY,则会出现这种情况。

    有关使用的任何覆盖索引的信息都显示在 EXPLAIN FORMAT=TRADITIONALEXPLAIN FORMAT=JSON 中。它也显示在 EXPLAIN FORMAT=TREE 中。

  • Using index condition(JSON 属性:using_index_condition

    通过访问索引元组并首先对其进行测试以确定是否读取完整的表行来读取表。通过这种方式,使用索引信息来延迟(下推)读取完整的表行,除非有必要。请参阅 第 10.2.1.6 节“索引条件下推优化”

  • Using index for group-by(JSON 属性:using_index_for_group_by

    Using index 表访问方法类似,Using index for group-by 表示 MySQL 找到了一个索引,该索引可用于检索 GROUP BYDISTINCT 查询的所有列,而无需对实际表进行任何额外的磁盘访问。此外,以最有效的方式使用索引,以便对于每个组,只读取几个索引条目。有关详细信息,请参阅 第 10.2.1.17 节“GROUP BY 优化”

  • Using index for skip scan(JSON 属性:using_index_for_skip_scan

    表示使用了跳跃扫描访问方法。请参阅 跳跃扫描范围访问方法

  • Using join buffer (Block Nested Loop)Using join buffer (Batched Key Access)Using join buffer (hash join)(JSON 属性:using_join_buffer

    来自先前连接的表的部分内容被读入连接缓冲区,然后使用缓冲区中的行与当前表执行连接。(块嵌套循环)表示使用块嵌套循环算法,(批量键访问)表示使用批量键访问算法,而(哈希连接)表示使用哈希连接。也就是说,来自EXPLAIN 输出的上一行的表的键被缓冲,并且匹配的行从使用连接缓冲区出现的行所表示的表中批量获取。

    在 JSON 格式的输出中,using_join_buffer 的值始终是块嵌套循环批量键访问哈希连接之一。

    有关哈希连接的更多信息,请参见第 10.2.1.4 节“哈希连接优化”

    有关批量键访问算法的信息,请参见批量键访问连接

  • 使用 MRR(JSON 属性:message

    使用多范围读取优化策略读取表。请参见第 10.2.1.11 节“多范围读取优化”

  • 使用 sort_union(...)使用 union(...)使用 intersect(...)(JSON 属性:message

    这些指示了显示如何为index_merge 连接类型合并索引扫描的特定算法。请参见第 10.2.1.3 节“索引合并优化”

  • 使用临时表(JSON 属性:using_temporary_table

    为了解析查询,MySQL 需要创建一个临时表来保存结果。如果查询包含列出不同列的GROUP BYORDER BY 子句,则通常会发生这种情况。

  • 使用 where(JSON 属性:attached_condition

    使用WHERE 子句来限制哪些行与下一个表匹配或发送到客户端。除非您特别打算从表中获取或检查所有行,否则如果Extra 值不是使用 where 并且表连接类型是ALLindex,则您的查询中可能存在错误。

    使用 where 在 JSON 格式的输出中没有直接对应的值;attached_condition 属性包含使用的任何WHERE 条件。

  • 使用带有下推条件的 where(JSON 属性:message

    此项仅适用于NDB 表。这意味着 NDB 集群正在使用条件下推优化来提高非索引列和常量之间直接比较的效率。在这种情况下,条件被“下推”到集群的数据节点,并在所有数据节点上同时进行评估。这消除了通过网络发送不匹配行的需要,并且可以使此类查询的速度比可以使用但未使用条件下推的情况快 5 到 10 倍。有关更多信息,请参见第 10.2.1.5 节“引擎条件下推优化”

  • 零限制(JSON 属性:message

    查询具有LIMIT 0 子句,并且不能选择任何行。

EXPLAIN 输出解释

您可以通过将EXPLAIN 输出的rows 列中的值相乘来很好地指示连接的好坏。这应该大致告诉您 MySQL 必须检查多少行才能执行查询。如果您使用max_join_size 系统变量限制查询,则此行积也将用于确定要执行哪些多表SELECT 语句以及要中止哪些语句。请参见第 7.1.1 节“配置服务器”

以下示例显示了如何根据EXPLAIN 提供的信息逐步优化多表连接。

假设您有如下所示的SELECT 语句,并且您计划使用EXPLAIN 来检查它

EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
               tt.ProjectReference, tt.EstimatedShipDate,
               tt.ActualShipDate, tt.ClientID,
               tt.ServiceCodes, tt.RepetitiveID,
               tt.CurrentProcess, tt.CurrentDPPerson,
               tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
               et_1.COUNTRY, do.CUSTNAME
        FROM tt, et, et AS et_1, do
        WHERE tt.SubmitTime IS NULL
          AND tt.ActualPC = et.EMPLOYID
          AND tt.AssignedPC = et_1.EMPLOYID
          AND tt.ClientID = do.CUSTNMBR;

对于此示例,请做出以下假设

  • 要比较的列已声明如下。

    数据类型
    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 语句会生成以下信息

table type possible_keys key  key_len ref  rows  Extra
et    ALL  PRIMARY       NULL NULL    NULL 74
do    ALL  PRIMARY       NULL NULL    NULL 2135
et_1  ALL  PRIMARY       NULL NULL    NULL 74
tt    ALL  AssignedPC,   NULL NULL    NULL 3872
           ClientID,
           ActualPC
      Range checked for each record (index map: 0x23)

因为每个表的type 都是ALL,所以此输出表明 MySQL 正在生成所有表的笛卡尔积;也就是说,每一行的组合。这需要相当长的时间,因为必须检查每个表中行数的乘积。对于当前的情况,这个乘积是 74 × 2135 × 74 × 3872 = 45,268,558,720 行。如果表更大,您可以想象它将花费多长时间。

这里的一个问题是,如果将列声明为相同的类型和大小,则 MySQL 可以更有效地使用列上的索引。在这种情况下,如果将VARCHARCHAR 声明为相同的大小,则它们被认为是相同的。tt.ActualPC 被声明为CHAR(10),而et.EMPLOYIDCHAR(15),因此存在长度不匹配。

要解决列长度之间的这种差异,请使用ALTER TABLEActualPC 的长度从 10 个字符增加到 15 个字符

mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);

现在tt.ActualPCet.EMPLOYID 都是VARCHAR(15)。再次执行EXPLAIN 语句会产生以下结果

table type   possible_keys key     key_len ref         rows    Extra
tt    ALL    AssignedPC,   NULL    NULL    NULL        3872    Using
             ClientID,                                         where
             ActualPC
do    ALL    PRIMARY       NULL    NULL    NULL        2135
      Range checked for each record (index map: 0x1)
et_1  ALL    PRIMARY       NULL    NULL    NULL        74
      Range checked for each record (index map: 0x1)
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC 1

这并不完美,但要好得多:rows 值的乘积减少了 74 倍。此版本在几秒钟内执行完毕。

可以进行第二次更改,以消除tt.AssignedPC = et_1.EMPLOYIDtt.ClientID = do.CUSTNMBR 比较的列长度不匹配

mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),
                      MODIFY ClientID   VARCHAR(15);

修改后,EXPLAIN 会生成如下所示的输出

table type   possible_keys key      key_len ref           rows Extra
et    ALL    PRIMARY       NULL     NULL    NULL          74
tt    ref    AssignedPC,   ActualPC 15      et.EMPLOYID   52   Using
             ClientID,                                         where
             ActualPC
et_1  eq_ref PRIMARY       PRIMARY  15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY  15      tt.ClientID   1

此时,查询几乎已优化到最佳状态。剩下的问题是,默认情况下,MySQL 假设tt.ActualPC 列中的值是均匀分布的,而tt 表的情况并非如此。幸运的是,很容易告诉 MySQL 分析键分布

mysql> ANALYZE TABLE tt;

使用附加的索引信息,连接是完美的,EXPLAIN 会生成以下结果

table type   possible_keys key     key_len ref           rows Extra
tt    ALL    AssignedPC    NULL    NULL    NULL          3872 Using
             ClientID,                                        where
             ActualPC
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC   1
et_1  eq_ref PRIMARY       PRIMARY 15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY 15      tt.ClientID   1

EXPLAIN 输出中的rows 列是 MySQL 连接优化器的 educated guess。通过将rows 乘积与查询返回的实际行数进行比较,检查这些数字是否接近真实值。如果数字相差很大,则您可能会通过在SELECT 语句中使用STRAIGHT_JOIN 并尝试以不同的顺序在FROM 子句中列出表来获得更好的性能。(但是,STRAIGHT_JOIN 可能会阻止使用索引,因为它禁用了半连接转换。请参见使用半连接转换优化 IN 和 EXISTS 子查询谓词。)

在某些情况下,当将EXPLAIN SELECT 与子查询一起使用时,可以执行修改数据的语句;有关更多信息,请参见第 15.2.15.8 节“派生表”