本文解释了如何使用 EXPLAIN ANALYZE,这是一个用于改进 MySQL 8 查询的实用工具。

大家好,
我是开发团队野生队的成员 Mandai。

这次,我将介绍如何使用 EXPLAIN ANALYZE,这是一个用于改进 MySQL 8 查询的实用工具。

在调整 MySQL 查询时,通常会使用 EXPLAIN,但 EXPLAIN 只显示查询执行计划,不提供详细信息,例如实际查询执行时间或行数。

但是,从 MySQL 8 开始,您可以使用 EXPLAIN ANALYZE 来发现仅靠 EXPLAIN 无法发现的信息。让我们一起来看看!

什么是解释分析?

EXPLAIN ANALYZE 是 MySQL 8 中引入的一项新功能,它是一个非常有用的查询调优工具。

EXPLAIN ANALYZE 和 EXPLAIN 之间的关键区别在于,EXPLAIN 显示基于优化器统计信息的查询执行计划,而 EXPLAIN ANALYZE 实际执行查询,从而可以准确地确定查询执行时间。

关键在于实际执行查询。只有实际运行查询,才能获得可靠的详细信息,例如查询执行时间和处理的行数。

此外,由于内部处理的每个阶段的执行时间都清晰地显示出来,开发人员可以轻松识别查询的哪个部分造成了瓶颈,这是一个非常受欢迎的功能。

由 EXPLAIN ANALYZE 提供的信息

使用 EXPLAIN ANALYZE 函数将为您提供以下信息:

解释 分析 选择 ...(省略)...\G *************************** 1. 行 *************************** EXPLAIN: -> 嵌套循环左连接(成本=10.2 行=2.13)(实际时间=0.192..3.42 行=63 循环=1) -> 嵌套循环左连接(成本=8.37 行=2.13)(实际时间=0.172..3.26 行=63 循环=1) -> 嵌套循环左连接(成本=6.22 行=0.208)(实际时间=0.153..2.77 行=63 循环=1) -> 嵌套循环左连接(成本=6.06 行=0.208)(实际时间=0.151..2.75 行=63 循环=1) -> 嵌套循环左连接(成本=5.92 行=0.0991) (实际时间=0.123..0.626 行数=21 循环数=1)-> 嵌套循环内连接(成本=5.86 行数=0.07)(实际时间=0.0448..0.065 行数=7 循环数=1)-> 嵌套循环内连接(成本=5.61 行数=0.7)(实际时间=0.0398..0.0569 行数=7 循环数=1)-> 筛选条件:((table1.id 在 ('52','48','58','201','59','45','54') 中)且(table1.cd1 不为空)且(table1.cd2 不为空))(成本=3.16 行数=7)(实际时间=0.0248..0.0383 行数=7 循环数=1)-> 使用 PRIMARY 对 table1 进行索引范围扫描(id = 45) 或 (id = 48) 或 (还有 5 个) (成本=3.16 行=7) (实际时间=0.0231..0.0311 行=7 循环=1) -> 筛选:(table2.flag1 = '1') (成本=0.251 行=0.1) (实际时间=0.0023..0.00235 行=1 循环=7) -> 使用 cd1 对 table2 进行单行索引查找 (cd1=table1.cd1) (成本=0.251 行=1) (实际时间=0.00199..0.002 行=1 循环=7) -> 筛选:(table3.flag1 = '1') (成本=0.264 行=0.1) (实际时间=902e-6..995e-6 行=1 循环=7) -> 单行使用 cd3 对表 3 进行索引查找 (cd3=table1.cd3) (成本=0.264 行数=1) (实际时间=762e-6..789e-6 行数=1 循环次数=7) -> 筛选条件:((table4.type = '1') 且 (table4.date <= table1.date) 且 (table4.date = (select #2))) (成本=2.8 行数=1.42) (实际时间=0.0509..0.0798 行数=3 循环次数=7) -> 使用 table5_idx2 对表 4 进行索引查找 (id3=table1.id3, date=(select #2)) (成本=2.8 行数=1.42) (实际时间=0.0367..0.0376 行数=3 循环次数=7) -> 选择 #2 (条件中的子查询;依赖项) -> 聚合:max(table7.date) (成本=1.17 行数=1)(实际时间=0.0134..0.0134 行数=1 循环次数=35)-> 筛选:(table7.type = '1')(成本=1.16 行数=0.0699)(实际时间=0.00638..0.0121 行数=4 循环次数=35)-> 使用 table5_idx1(id3=table1.id3)对 table7 进行索引查找,索引条件为:(table7.date <= table1.date)(成本=1.16 行数=2.1)(实际时间=0.00624..0.0116 行数=6.43 循环次数=35)-> 筛选:((table6.type = '2')且(table6.date >= table1.date)且(table6.date =(选择 #3)))(成本=3.27 行数=2.1)(实际时间=0.00638..0.0121 行数=4 循环次数=35)时间=0.0216..0.101 行数=3 循环次数=21) -> 使用表5_idx1 (id3=table1.id3) 对表6进行索引查找 (成本=3.27 行数=2.1) (实际时间=0.00552..0.0129 行数=10 循环次数=21) -> 选择 #3 (条件中的子查询;依赖项) -> 聚合:min(table8.date) (成本=1.17 行数=1) (实际时间=0.0138..0.0138 行数=1 循环次数=126) -> 筛选:(table8.type = '2') (成本=1.16 行数=0.0699) (实际时间=0.00603..0.0124 行数=6 循环次数=126) -> 使用表5_idx1 (id3=table1.id3) 对表8进行索引查找,使用索引条件:(table8.date >= table1.date) (成本=1.16 行数=2.1) (实际时间=0.00577..0.0118 行数=7.71 循环次数=126) -> 使用非索引对表9进行单行索引查找 (no=table6.data) (成本=1.19 行数=1) (实际时间=89.6e-6..89.6e-6 行数=0 循环次数=63) -> 筛选条件:(table10.type = '1') (成本=14.2 行数=10.2) (实际时间=0.00681..0.00772 行数=1 循环次数=63) -> 使用表10_idx1 (id3=table10.id3) 对表10进行索引查找 (成本=14.2 行数=10.2) (实际时间=0.00667..0.00735) rows=5 loops=63) -> 使用 id4 对表 11 进行索引查找 (id4=table11.id4) (成本=0.797 rows=1) (实际时间=0.00203..0.00231 rows=1 loops=63) 1 行结果,4 个警告 (0.007 秒)

看起来好像有很多 JOIN 操作,但我特意创建了一个复杂的查询。
如果您习惯于查看 EXPLAIN 的输出,可能会觉得 EXPLAIN ANALYZE 的输出有点难以理解,但这个输出包含大量信息,可以帮助您提高查询性能!

巧合的是,我们有一个同时使用了 LEFT JOIN 和 INNER JOIN 的查询。结合 MySQL 的嵌套循环连接机制来看,我们可以看到 LEFT JOIN 表中的记录被放在循环外部,而 INNER JOIN 表中的记录被放在循环内部,然后两个表被连接起来。

实际上,此查询中 INNER JOIN 和 LEFT JOIN 的顺序被打乱了,但优化器能够正确地完成其工作,处理顺序是正确的,从 LEFT JOIN 的表开始。

每个进程成本行数实际时间循环次数。由于您可以查看处理数据的实际时间和处理的行数,因此可以确定查询的哪个部分导致了瓶颈。

各数值的含义如下:

左侧括号内的数字

  • 成本:查询优化器估算的成本
  • rows :优化器估计的行数。

右侧括号内的数字

  • 实际时间:实际耗时。“.”左边的时间是返回第一行所需的时间,右边的时间是返回最后一行所需的时间。
  • 行数:实际处理的行数。
  • 循环次数:该过程已执行的次数

例如,如果我们看一下第一行“嵌套循环左连接”,它显示(成本=10.2 行=2.13)(实际时间=0.192..3.42 行=63 循环=1)

这意味着查询优化器估计此过程的成本为 10.2 ( cost=10.2 ,并预测将处理 2.13 行 (
rows=2.13 相比之下,实际时间为 0.192 至 3.42毫秒,这意味着返回第一行耗时 0.192 毫秒,返回最后一行耗时 3.42 毫秒。
处理最外层最后一行所花费的时间可以大致视为查询的总执行时间,但这并不包括通过网络返回结果所花费的时间。
如果您觉得查询执行时间过长,即使 EXPLAIN ANALYZE 的结果良好,则可能是通过网络返回结果所花费的时间是一个重要因素。

此外,虽然实际处理了 63 行(
rows=63) 这表明优化器的统计信息可能已过时,因此最好运行 ANALYZE TABLE 来更新统计信息。

顺便一提,这次的 EXPLAIN ANALYZE 测试是在我的本地环境中运行的,所以数据量并不大。
因此,单看这个结果,你可能会觉得它快得惊人!但是请注意,除非你准备的数据量与生产环境的数据量相当,否则你将无法获得准确的信息。

对于简单的索引检查,EXPLAIN 的输出信息较少,因此更容易阅读。但是,当需要提升查询性能时(例如索引未按预期工作时),使用 EXPLAIN ANALYZE 来检查详细信息(例如实际查询执行时间和处理的行数)有时可以提供明确的解决方案。

概括

这次,我介绍了一种使用 EXPLAIN ANALYZE 来改进查询的方法,EXPLAIN ANALYZE 是 MySQL 8 中新增的功能。

尽管名为“新”,但添加了 EXPLAIN ANALYZE 的 MySQL 8.0.18 版本早在 2019 年就发布了,所以严格来说并不算“新”。然而,似乎仍然有很多人尝试改进他们的查询,但仅仅查看 EXPLAIN 却仍然一头雾水,最终放弃。因此,我决定重新撰写这篇文章。

如果事先没有明确定义应用程序规范,并且在开发过程中追溯添加列,则可能会遇到索引无法有效工作的查询,或者使用笨拙的 JOIN 来检索数据的查询。

重点是,在调整此类查询时,您可能需要尝试使用 EXPLAIN ANALYZE。

就这样。

如果您觉得这篇文章有用,请点击【点赞】!
0
加载中...
0票,平均分:0.00/10
2
X Facebook Hatena书签 口袋

这篇文章的作者

关于作者

万代洋一

我的主要工作是开发社交游戏的Web API,但我也很荣幸能够从事其他各种工作,包括市场营销。
我在Beyond中的肖像权采用CC0协议。