

在 MySQL 中,EXPLAIN 是一个关键字,用于了解查询执行的相关信息。本文将展示如何利用MySQL EXPLAIN 来解决查询中的性能问题。
虽然执行一个 EXPLAIN 计划相对简单,但其输出结果并不总是直观的。只有了解其功能,才能充分利用它来实现SQL语句的性能提升。
当在查询的前面添加 EXPLAIN 关键字时,它会解释数据库如何执行该查询以及估算的成本。
示例EXPLAIN语句:

explain demo
通过利用这个MySQL 内部工具,可以观察到以下内容:
总之,通过使用 EXPLAIN,可以获得查询预期运行的步骤列表。
在 MySQL 8.0.18 中,MySQL 引入了 EXPLAIN ANALYZE,一个在常规 EXPLAIN 查询计划工具之上的新功能。除了列出查询计划和估算的成本,EXPLAIN ANALYZE 还打印了执行计划中各个迭代器的实际成本。
示例EXPLAIN ANALYZE语句:

explain analyze
注意事项:EXPLAIN ANALYZE 实际上会运行查询,因此如果你不希望查询在实时数据库上运行,请不要使用 EXPLAIN ANALYZE。
对于每个迭代器,EXPLAIN ANALYZE 提供以下信息:
MySQL EXPLAIN ANALYZE 的结果会显示查询运行前规划器的估算数据(如图中黄色突出显示部分)和查询实际运行后的数据(如绿色突出显示部分)。
EXPLAIN ANALYZE 可用于 SELECT 语句、多表 UPDATE 语句、DELETE 语句和 TABLE 语句。它会自动选择 FORMAT=tree 并执行查询(不会向用户显示任何输出)。EXPLAIN ANALYZE 专注于查询执行的关系以及部分查询的执行顺序。
EXPLAIN 输出以节点形式组织。在最低层,节点会扫描表或搜索索引;较高层的节点则操作来自低层节点的结果。
虽然 MySQL CLI 能以表格、制表符、垂直格式,以及漂亮或原始 JSON 格式打印 EXPLAIN 结果,但目前 EXPLAIN ANALYZE 不支持 JSON 格式。
当你不确定查询是否高效运行时,可以(且应)使用 EXPLAIN 查询。如果你认为已经正确索引并分区了表,但查询依旧运行缓慢,则可能需要使用EXPLAIN ANALYZE了。当查询进行EXPLAIN ANALYZE之后,就需要关注的输出内容以及优化目标了。
在 MySQL 中处理索引时,需关注 possible_keys、key 和 key_len 列。
这些信息对设计索引、为特定任务决定使用何种索引,以及处理相关问题(如选择覆盖索引的适当长度)非常实用。
当使用 FULLTEXT 索引确保查询参与 JOIN 操作时,需注意 select_type 列,该列的值应为 fulltext。
如果表已添加分区并希望查询使用这些分区,要观察 partition 列。如果 MySQL 实例正在使用分区,在大多数情况下,MySQL 会自动处理所有查询,而无需额外操作。如果希望查询使用特定分区,可以使用类似 SELECT * FROM TABLE_NAME PARTITION(p1,p2) 的查询。
EXPLAIN 是一种估算工具。它有时是一个比较准确的估算,但有时可能非常不精确。以下是一些局限性:
filesort 表示内存排序和磁盘排序,用 Using temporary 表示内存临时表和磁盘临时表。需要注意的一点是:如果你用 EXPLAIN 的查询未正确解析,可以输入 SHOW WARNINGS; 查看最后一个运行的非诊断语句的信息。虽然它无法提供像 EXPLAIN 那样的查询执行计划,但它可能提供关于可处理的查询片段的线索。
SHOW WARNINGS; 包含一些特殊标记,其中信息可能包括:
<index_lookup>(query fragment):表明如果查询正确解析会进行索引查找。<if>(condition, expr1, expr2):表明该查询特定部分有 IF 条件。<primary_index_lookup>(query fragment):表明通过主键进行索引查找。<temporary table>:表明这里会创建内部表以保存临时结果(例如在连接之前的子查询中)。MySQL 手册提到 type 列显示“连接类型”,用以解释表的连接方式。但实际上更准确的说法是“访问类型”,即告诉我们 MySQL 决定如何在表中找到行。
以下列出从性能最佳到最差的重要访问方式:
ref_or_null 是 ref 的变种,表示初次查找后需再查找 NULL 条目。还有一些其他类型需要了解:
eq_ref。通常用于以下形式的子查询:value IN (SELECT primary_key FROM single_table WHERE some_expr)。unique_subquery 类似,但应用于非唯一索引的子查询。EXTRA 列包含其他列中未涵盖的额外信息。以下是一些重要值及其定义:
下面通过一个实践案例来演示一下使用 MySQL EXPLAIN 优化查询的方法。
在开始之前,先创建一个数据库,并使用MySQL员工样例数据库进行初始化。
通过使用多列索引和MySQL EXPLAIN,允许数据库引擎联合使用多列加速查询。
例如,优化下列查询:
SELECT * FROM employees WHERE last_name = 'Puppo' AND first_name = 'Kendra';

explain demo
在运行该查询后,EXPLAIN 的结果显示访问了 299,733 行,而这是我们需要优化以提升性能的根本原因。
一种方法是分别为 last_name 列和 first_name 列创建单独索引,但这种方式有一个问题——MySQL 知道如何找到所有姓 Puppo 的员工,也知道如何找到所有名为 Kendra 的员工,但却无法同时高效找到名为 Kendra Puppo 的员工。
其他需要注意的事项:
由于第一种方法的问题,我们知道需要找到一种解决方案来使用能够考虑多列的索引。这里我们可以使用多列索引来实现这一目标。
可以将其想象成一本电话簿嵌套在另一本电话簿中。首先,查阅姓氏 “Puppo”,然后进入第二个目录,该目录按名字的字母顺序组织所有名为 “Kendra” 的人,在这个目录中可以快速找到“Kendra Puppo”。
在 MySQL 中,若要为 employees 表中的姓氏和名字创建多列索引,可以执行以下命令:
CREATE INDEX fullnames ON employees(last_name, first_name);
现在,多列索引已成功创建,我们可以执行以下 SELECT 查询来查找名字为 Kendra 且姓氏为 Puppo 的记录。结果将是一行数据,其中包含名为 Kendra Puppo 的员工信息。
使用 EXPLAIN 来检查该查询是否使用了索引:

explain result
查询优化后的结果显示,索引被使用,并且只访问了一行数据来完成请求。这比优化前必须访问 299,733 行要高效得多。
MySQL 的 EXPLAIN 语句可以用来获取查询执行的信息,在设计架构或索引时非常有价值。利用 MySQL 提供的功能进行优化,可以大幅提升数据库性能。
全文完,感谢阅读,如果喜欢请三连。