MySQL Workbench 手册  /  性能工具  /  教程:使用 Explain 提高查询性能

7.5 教程:使用 Explain 提高查询性能

本教程介绍如何使用 Explain 报告查找和修复有问题的(缓慢的)查询。它使用 DBT-3 数据库,并从以下简单的查询示例开始。

SELECT * FROM orders
WHERE YEAR(o_orderdate) = 1992 AND MONTH(o_orderdate) = 4
AND o_clerk LIKE '%0223';

如下图所示,查询示例首先在可视化 SQL 编辑器中执行。接下来,通过单击 查询 菜单中的 Explain 当前语句 生成 Explain 报告。初始报告显示了一个可视化 Explain 图像,其中包含将指针设备移动到全表扫描中的 orders 表上时出现的信息。

图 7.9 DBT-3 Explain 教程:全表扫描的可视化 Explain

Content is described in the surrounding text.

您也可以切换到表格 Explain,如下图所示。使用下拉列表在可视化和表格表示之间切换。

图 7.10 DBT-3 Explain 教程:全表扫描的表格 Explain

Content is described in the surrounding text.

关于查询的问题

  • 为什么此查询会生成全表扫描?

  • 为什么索引 o_orderdate 列没有作为可能的键出现?

仔细观察,还会注意到索引列正在表达式 "WHERE YEAR(o_orderdate) = 1992 AND MONTH(o_orderdate) = 4" 中使用,因此未使用索引。要使用现有索引,您可以按如下方式调整查询。

SELECT * FROM orders
WHERE o_orderdate BETWEEN '1992-04-01' AND '1992-04-30'
AND o_clerk LIKE '%0223';

更新后的查询示例会在可视化 Explain 图像中生成 索引范围扫描,替换上一个查询示例生成的 全表扫描。接下来的两张图显示了修改后的查询示例的可视化和表格表示。

图 7.11 DBT-3 Explain 教程:索引范围扫描的可视化 Explain

Content is described in the surrounding text.

图 7.12 DBT-3 Explain 教程:索引范围扫描的表格 Explain

Content is described in the surrounding text.

请注意差异。“类型”从 ALL 更改为 range,可能的键(和使用的键)从 NULL 更改为 i_o_orderdate,扫描的行数从 150 万更改为约 3.3 万。但是,扫描 3.3 万行而只返回 18 行是不必要的,因此可以将重点转移到 o_clerk 列。下一个查询示例(和表格 Explain 图)添加了以下索引,这些索引应该可以提高性能。

CREATE INDEX i_o_clerk ON orders(o_clerk);

图 7.13 DBT-3 Explain 教程:索引范围扫描和索引后的表格 Explain

Content is described in the surrounding text.

新索引未被视为可能的键,因为查询正在搜索 o_clerk 列的后缀,而索引不适用于后缀(尽管它们适用于前缀)。相反,这个简单的例子可以使用完整的业务员 ID。按如下方式调整查询将显示更好的结果。

SELECT * FROM orders
WHERE o_orderdate BETWEEN '1992-04-01' AND '1992-04-30'
AND o_clerk LIKE 'Clerk#000000223';

以下数字分别表示可视化 Explain 和表格 Explain 中更新后的查询示例的效果。

图 7.14 DBT-3 Explain 教程:索引范围扫描和完整 ID 的可视化 Explain

Content is described in the surrounding text.

图 7.15 DBT-3 Explain 教程:索引范围扫描和完整 ID 的表格 Explain

Content is described in the surrounding text.

o_clerk 索引被考虑和使用,查询扫描了 1546 行而不是 32642 行,查询执行时间从 0.281 秒提高到 0.234 秒。但是,EXPLAIN 估计此查询扫描 1546 行以返回 18 行。再次查看查询后,请考虑多列索引可以满足基于 o_orderdateo_clerk 列的 WHERE 子句的条件,如下一个语句所示。

CREATE INDEX io_clerk_date ON orders(o_clerk, o_orderdate)
注意

o_clerk 在索引中显示为第一列,因为 o_orderdate 使用范围。

现在,执行调整后的查询会产生更好的结果。估计扫描并返回了 18 行,查询示例的执行时间为 0.234 秒,如下一个可视化 Explain 和表格 Explain 图所示。

图 7.16 DBT-3 Explain 教程:多列索引范围扫描的可视化 Explain

Content is described in the surrounding text.

图 7.17 DBT-3 Explain 教程:多列索引范围扫描的表格 Explain

Content is described in the surrounding text.

下表总结了本教程中对查询所做修改的结果。

表 7.2 DBT-3 Explain 教程查询比较

类型 可能的键 扫描的行数 持续时间(秒) 额外信息 返回的行数
all NULL NULL 1.50M 1.201 使用 where 18
range i_o_orderdate i_o_orderdate 32642 0.281 使用索引条件;使用 where 18
range i_o_orderdate, i_o_clerk i_o_clerk 1546 0.234 使用索引条件;使用 where 18
range i_o_orderdate, i_o_clerk, i_o_clerk_date i_o_clerk_date 18 0.234 使用索引条件 18