• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 解释输出格式

    EXPLAIN语句提供有关MySQL如何执行语句的信息。EXPLAIN作品有SELECTDELETEINSERTREPLACE,和UPDATE语句。

    EXPLAINSELECT语句中使用的每个表返回一行信息。它按照MySQL在处理语句时读取它们的顺序列出了输出中的表。MySQL使用嵌套循环连接方法解析所有连接。这意味着MySQL从第一个表中读取一行,然后在第二个表,第三个表中找到匹配的行,依此类推。处理完所有表后,MySQL将通过表列表输出选定的列和回溯,直到找到一个表,其中存在更多匹配的行。从该表中读取下一行,然后继续下一个表。

    注意

    MySQL Workbench具有可视解释功能,可提供EXPLAIN输出的可视表示。请参阅教程:使用解释来提高查询性能。

    • 解释输出列
    • 说明联接类型
    • 了解更多信息
    • 解释输出解释

    解释输出列

    本节介绍产生的输出列EXPLAIN。后面的部分提供有关typeExtr 列的其他信息。

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

    表8.1 EXPLAIN输出列

    JSON名称含义
    idselect_idSELECT标识符
    select_type没有SELECT类型
    tabletable_name输出行表
    partitionspartitions匹配的分区
    type ccess_type联接类型
    possible_keyspossible_keys可能的索引选择
    keykey实际选择的索引
    key_lenkey_length所选键的长度
    refref与索引比较的列
    rowsrows估计要检查的行
    filteredfiltered按表条件过滤的行百分比
    Extr 没有附加信息

    注意

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

    • id(JSON名:select_id

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

    • select_type(JSON名称:无)

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

      select_typeJSON名称含义
      SIMPLE没有简单SELECT(不使用UNION或子查询)
      PRIMARY没有最外层SELECT
      UNION没有SELECT陈述中的第二条或更高条UNION
      DEPENDENT UNIONdependenttrue中的第二个或更高版本的SELECT语句UNION,取决于外部查询
      UNION RESULTunion_result结果UNION
      SUBQUERY没有首先SELECT在子查询
      DEPENDENT SUBQUERYdependenttrue首先SELECT在子查询中,取决于外部查询
      DERIVED没有派生表
      DEPENDENT DERIVEDdependenttrue派生表依赖于另一个表
      MATERIALIZEDmaterialized_from_subquery物化子查询
      UNCACHEABLE SUBQUERYcacheablefalse子查询,其结果无法缓存,必须针对外部查询的每一行重新进行评估
      UNCACHEABLE UNIONcacheablefalseUNION属于不可缓存子查询的中的第二个或更高版本的选择(请参阅参考资料UNCACHEABLE SUBQUERY

      DEPENDENT通常表示使用相关子查询。请参见“相关子查询”。

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

      当您指定FORMAT=JSONwith时EXPLAIN,输出没有直接等同于select_type;的单个属性。该query_block属性对应于给定SELECT。相当于大部分的性质SELECT只是示出子查询类型是可用的(一个例子是materialized_from_subquery用于MATERIALIZED),并且当被显示为宜。没有SIMPLE或的JSON等效项PRIMARY

      select_typeSELECT语句的值显示受影响表的语句类型。例如,select_typeDELETEDELETE报表。

    • table(JSON名:table_name

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

      • <unionM,N>:该行是指具有和id值的行的M并集N
      • <derivedN>:该行是指用于与该行的派生表结果id的值N。派生表可能来自(例如)FROM子句中的子查询。
      • <subqueryN>:该行是指该行的物化子查询的结果,其id值为N。请参见“通过实现来优化子查询”。
    • partitions(JSON名:partitions

      查询将从中匹配记录的分区。该值适用NULL于未分区的表。请参见“获取有关分区的信息”。

    • type(JSON名: ccess_type

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

    • possible_keys(JSON名:possible_keys

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

      如果此列是NULL(或在JSON格式的输出中未定义),则没有相关的索引。在这种情况下,您可以通过检查该WHERE子句以检查它是否引用了某些适合索引的列,从而提高查询性能。如果是这样,请创建一个适当的索引并EXPLAIN再次检查查询。请参见“ 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在您的查询。请参见“索引提示”。

      对于MyISAM表,运行ANALYZE TABLE有助于优化器选择更好的索引。对于MyISAM表,myisamchk --analyze也是如此。请参见“ ANALYZE TABLE语句”和“ 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。

    • Extr (JSON名称:无)

      此列包含有关MySQL如何解析查询的其他信息。有关不同值的说明,请参见《EXPLAIN额外信息》。

      Extr 列没有对应的JSON属性;但是,此列中可能出现的值显示为JSON属性或该message属性的文本。

    说明联接类型

    typeEXPLAIN输出介绍如何联接表。在JSON格式的输出中,这些作为 ccess_type属性的值找到。以下列表描述了连接类型,从最佳类型到最差类型:

    • system

      该表只有一行(=系统表)。这是const联接类型的特例。

    • const

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

      const在将a PRIMARY KEYUNIQUE索引的所有部分与常数值进行比较时使用。在以下查询中,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类型,这是最好的联接类型。当联接使用索引的所有部分并且索引为a PRIMARY KEYUNIQUE NOT NULLindex时使用。

      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

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

      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;
      

      请参见“ IS NULL优化”。

    • index_merge

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

    • unique_subquery

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

      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包含已使用的时间最长的关键部分。该refNULL适用于此类型。

      range当一个键列使用任何的相比于恒定可使用=<>>>=<<=IS NULL<=>BETWEENLIKE,或IN()运营商:

      SELECT * FROM tbl_name
        WHERE key_column = 10;
      
      SELECT * FROM tbl_name
        WHERE key_column BETWEEN 10 nd 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,只是索引树被扫描。这发生两种方式:

      • 如果索引是查询的覆盖索引,并且可用于满足表中所需的所有数据,则仅扫描索引树。在这种情况下,Extr 列显示为Using index。仅索引扫描通常比索引扫描更快,ALL因为索引的大小通常小于表数据。
      • 使用对索引的读取执行全表扫描,以按索引顺序查找数据行。Uses index没有出现在Extr 列中。

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

    • ALL

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

    了解更多信息

    Extr EXPLAIN输出包含MySQL解决查询的额外信息。以下列表说明了可以在此列中显示的值。每个项目还为JSON格式的输出指示哪个属性显示Extr 值。对于其中一些,有一个特定的属性。其他显示为message属性的文本。

    如果你想使你的查询尽可能快,看出来Extr 的列值Using filesortUsing temporary,或在JSON格式的EXPLAIN输出,用于using_filesortusing_temporary_table性能等于true

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

      将该表引用为table可以下推到NDB内核的联接中的子级。启用下推联接时,仅适用于NDB群集。有关ndb_join_pushdown更多信息和示例,请参见服务器系统变量的描述。

    • const row not found(JSON属性:const_row_not_found

      对于诸如之类的查询,该表为空。SELECT ... FROM tbl_name

    • Deleting all rows(JSON属性:message

      对于DELETE,某些存储引擎(如MyISAM)支持一种处理程序方法,该方法以一种简单而快速的方式删除所有表行。Extr 如果引擎使用此优化,则显示此值。

    • 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

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

    • 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,在分区修剪后,优化器未发现任何要删除或更新的内容。它的含义类似于Impossible WHERE for SELECT语句。

    • No tables used(JSON属性:message

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

      对于INSERTREPLACE语句,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条件的一行后,不检查该表中的更多行是否为上一行。这是可以通过这种方式优化的查询类型的示例:

      SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id
        WHERE t2.id IS NULL;
      

      假设t2.id定义为NOT NULL。在这种情况下,MySQL 使用的值扫描t1并查找行。如果MySQL在中找到匹配的行,它将知道它永远不会是,并且不会扫描具有相同值的其余行。换句话说,对于in中的每一行,MySQL 实际上只需进行一次查找,无论in中实际匹配多少行。t2t1.idt2t2.idNULLt2idt1t2t2

      在MySQL 8.0.17及更高版本中,这还可以指示WHERE形式为的条件或已在内部转换为反联接的条件。这将删除子查询,并将其表放入最顶层查询的计划中,从而提供了改进的成本计划。通过合并半联接和反联接,优化器可以更自由地对执行计划中的表进行重新排序,在某些情况下,可以使计划更快。NOT IN(subquery)NOT EXISTS(subquery)

      通过检查或执行的结果后的Message列,您可以参见何时对给定查询执行反联接转换。SHOW WARNINGSEXPLAINEXPLAIN FORMAT=TREE

      注意

      反连接是半连接的补充。反联接返回所有行,其中没有匹配的行。table_ JOIN table_b ON conditiontable_ table_bcondition

    • Plan isn't ready yet(JSON属性:无)

      EXPLAIN FOR CONNECTION当优化程序尚未完成为在命名连接中执行的语句创建执行计划时,就会出现此值。如果执行计划输出包含多行,则Extr 取决于优化程序确定完整执行计划的进度,其中任何一行或所有行都可以具有此值。

    • Range checked for each record(index map:N)(JSON属性:message

      MySQL没有找到合适的索引来使用,但是发现一些索引可以在已知先前表中的列值之后使用。对于上表中的每个行组合,MySQL检查是否可以使用rangeindex_merge访问方法来检索行。这不是很快,但是比完全没有索引的连接要快。适用标准如“范围优化”和“索引合并优化”中所述,除了上表的所有列值都是已知的并且被视为常量。

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

    • Recursive(JSON属性:recursive

      这表明该行适用于SELECT递归公用表表达式的递归部分。请参见“ WITH(公用表表达式)”。

    • Rematerialize(JSON属性:rematerialize

      Rematerialize(X,...)EXPLAINtable 的行中显示T,其中X是任何横向派生表,当T读取新行时会触发其重新实现。例如:

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

      每当t顶级查询处理新的一行时,都会重新实现派生表的内容,以使其保持最新状态。

    • Scanned N databases(JSON属性:message

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

    • Select tables optimized away(JSON属性:message

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

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

      考虑以下隐式分组查询:

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

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

      Extr 如果要读取的行不是确定性的,则不会出现此值。考虑以下查询:

      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),对于缺少该子句或始终为true且没有子句的查询,Extr 可能会出现此值。(这是一个隐式分组查询的实例,其中存储引擎影响是否可以读取确定数量的行。)COUNT(*)WHEREGROUP 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_nameUNIQUEPRIMARY KEY

    • Using filesort(JSON属性:using_filesort

      MySQL必须额外进行一遍,以找出如何按排序顺序检索行。排序是通过根据联接类型遍历所有行并存储与该WHERE子句匹配的所有行的排序键和指向该行的指针来完成的。然后对键进行排序,并按排序顺序检索行。请参见“通过优化排序”。

    • Using index(JSON属性:using_index

      仅使用索引树中的信息从表中检索列信息,而不必进行其他查找以读取实际行。当查询仅使用属于单个索引的列时,可以使用此策略。

      对于InnoDB具有用户定义的聚集索引的表,即使列中Using index不存在该索引也可以使用Extr 。如果typeis indexkeyis 就是这种情况PRIMARY

    • Using index condition(JSON属性:using_index_condition

      通过访问索引元组并首先对其进行测试以确定是否读取完整的表行来读取表。这样,除非有必要,否则索引信息将用于延迟(“下推”)读取整个表行。请参见“索引条件下推优化”。

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

      Using index表访问方法类似,Using index for group-by表示MySQL找到了一个索引,该索引可用于检索a GROUP BYDISTINCT查询的所有列,而无需对实际表进行任何额外的磁盘访问。此外,以最有效的方式使用索引,因此对于每个组,仅读取少数索引条目。有关详细信息,请参见“优化分组(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)(JSON属性:using_join_buffer

      来自较早联接的表被部分读取到联接缓冲区中,然后从缓冲区中使用它们的行来执行与当前表的联接。(Block Nested Loop)表示使用块嵌套循环算法,并(Batched Key Access)表示使用批处理密钥访问算法。也就是说,EXPLAIN将缓冲输出的前一行中的表中的键,并且将从Using join buffer出现的行所代表的表中批量提取匹配的行。

      在JSON格式的输出中,的值using_join_buffer始终为Block Nested Loop或之一Batched Key Access

    • Using MRR(JSON属性:message

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

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

      这些指示了特定算法,该算法显示了如何针对index_merge联接类型合并索引扫描。请参见“优化索引”。

    • Using temporary(JSON属性:using_temporary_table

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

    • Using where(JSON属性: ttached_condition

      WHERE子句用于限制来匹配下一个表或发送到客户端的行。除非您专门打算从表中获取或检查所有行,否则如果查询中的Extr 值不是Using where且表连接类型为ALL或,则查询中可能会出错index

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

    • Using where with pushed condition(JSON属性:message

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

    • Zero limit(JSON属性:message

      该查询有一个LIMIT 0子句,不能选择任何行。

    解释输出解释

    通过获取输出rows列中值的乘积,可以很好地表明联接的良好程度EXPLAIN。这应该大致告诉您MySQL必须检查多少行才能执行查询。如果使用max_join_size系统变量限制查询,则此行乘积还用于确定SELECT执行哪些多表语句以及中止哪个多表语句。请参见“配置服务器”。

    以下示例显示了如何根据提供的信息逐步优化多表联接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;
    

    对于此示例,进行以下假设:

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

      数据类型
      ttActualPCCHAR(10)
      ttAssignedPCCHAR(10)
      ttClientIDCHAR(10)
      etEMPLOYIDCHAR(15)
      doCUSTNMBRCHAR(15)
    • 这些表具有以下索引。

      指数
      ttActualPC
      ttAssignedPC
      ttClientID
      etEMPLOYID(首要的关键)
      doCUSTNMBR(首要的关键)
    • 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)
    

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

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

    若要解决此列长度之间的差异,请使用从10个字符ALTER TABLE延长ActualPC到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
    

    rows从输出列EXPLAIN是一个受过教育的猜测从MySQL联接优化。通过将rows乘积与查询返回的实际行数进行比较,检查数字是否接近真实值。如果数字完全不同,则可以通过STRAIGHT_JOINSELECT语句中使用并尝试在FROM子句中以不同顺序列出表来获得更好的性能。(但是,STRAIGHT_JOIN由于禁用了半联接转换,可能会阻止使用索引。请参见“使用 EXISTS 策略优化子查询”))

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