LabHub
学习 学习路径 课程

SQL 实战

解剖执行计划

在 LabHub 中继续学习

目标

从头到尾阅读 EXPLAIN 输出,熟悉应按什么顺序查看哪些数字。

为什么重要

执行计划中的节点名称本身并不能说明好坏。Seq Scan 明显可能比索引扫描更快,Nested Loop 也常常是最佳选择。优化器在已有统计信息范围内几乎总会作出合理判断。

因此,计划看似异常时,不应问“优化器为什么这么笨”,而应问:**“我向优化器提供了什么错误信息?”**估算行数与实际行数的差距会给出答案。

阅读顺序如下:先看 Execution Time 与 Planning Time;然后比较每个节点的估算 rows 与实际 rows,找到差距达到一个数量级以上的最内层节点;loops 不为 1 时通过乘法计算真实贡献;在 Rows Removed by Filter 较大的节点寻找索引机会;最后根据 Buffers 的 read 比例区分计划问题与缓存问题。

步骤

所有输出文件保存到 /root/exp/

  1. 为任意查询 orders 的语句只添加 EXPLAIN,执行后把输出保存到 /root/exp/plan_basic.txt不得包含实测值(actual time)。
  2. 为同一查询添加真实执行选项,保存到 /root/exp/plan_analyze.txt。必须包含 actual timeExecution Time
  3. 把包含块读取统计的 join 查询计划保存到 /root/exp/plan_buffers.txt,必须有 Buffers: shared ... 行。
  4. 创建表 skewed_events,列为 idkind,至少 10 万行,其中 kindrare 的行不超过总数 1%。创建后必须收集统计信息,让 planner 知道行数。
  5. 暂时关闭其他 join 方法后执行 join 查询,把计划保存到 /root/exp/plan_loops.txt。必须包含 Nested Loop,且 loops= 至少为三位数。
  6. 实测执行会选择 hash join 的查询,保存到 /root/exp/plan_hash.txt。必须包含 Hash JoinBatches:
  7. 分别在默认状态和禁用 sequential scan 的状态下实测执行同一查询,把两个结果依次追加到 /root/exp/plan_forced.txt。必须包含 Seq Scan、使用索引的节点,并至少两次出现 Execution Time
  8. 实测执行条件由 Index Cond 处理、且没有 Rows Removed by Filter 的计划,保存到 /root/exp/plan_fixed.txt

参考

不执行,只查看计划

为任意查询 orders 的语句只添加 EXPLAIN,执行后把输出保存到 /root/exp/plan_basic.txt不得包含实测值(actual time)。

不加选项,只添加一个关键字,就不会执行查询,只显示估算值。

查看带实测值的计划

为同一查询添加真实执行选项,保存到 /root/exp/plan_analyze.txt。必须包含 actual timeExecution Time

有一个选项会真实执行并测量。用于写查询时,请放在事务中。

启用块读取统计

把包含块读取统计的 join 查询计划保存到 /root/exp/plan_buffers.txt,必须有 Buffers: shared ... 行。

有一个选项可以区分缓存命中与磁盘读取。括号中可以用逗号列出多个选项。

创建偏斜数据表并收集统计

创建表 skewed_events,列为 idkind,至少 10 万行,其中 kindrare 的行不超过总数 1%。创建后必须收集统计信息,让 planner 知道行数。

用 generate_series 创建至少 10 万行,并让特定值以不超过 1% 的比例稀少出现。创建后必须收集统计信息,planner 才会知道。

观察高循环次数的 Nested Loop

暂时关闭其他 join 方法后执行 join 查询,把计划保存到 /root/exp/plan_loops.txt。必须包含 Nested Loop,且 loops= 至少为三位数。

暂时禁用其他 join 方法后会选择 Nested Loop。请检查计划中的 loops 值。

检查 Hash Join 的批次数

实测执行会选择 hash join 的查询,保存到 /root/exp/plan_hash.txt。必须包含 Hash JoinBatches:

Hash 节点的实测信息只有真实执行后才会出现。请确认 Batches 是 1 还是更大。

实测比较强制前后

分别在默认状态和禁用 sequential scan 的状态下实测执行同一查询,把两个结果依次追加到 /root/exp/plan_forced.txt。必须包含 Seq Scan、使用索引的节点,并至少两次出现 Execution Time

分别在默认状态和禁用 sequential scan 后运行同一查询,并把结果追加到同一个文件。

把 Filter 移到 Index Cond

实测执行条件由 Index Cond 处理、且没有 Rows Removed by Filter 的计划,保存到 /root/exp/plan_fixed.txt

目标是从一开始就不读取,而不是读取后丢弃。计划中的 Rows Removed by Filter 行必须消失。