Redrock Postgres 搜索 英文
版本: 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

15.3. 并行计划 #

15.3.1. 并行扫描
15.3.2. 并行连接
15.3.3. 并行聚合
15.3.4. 并行附加
15.3.5. 并行计划提示

由于每个工作线程都会执行计划的并行部分直到完成,因此无法简单地采取一个普通的查询计划并使用多个工作线程来运行它。每个工作线程都会生成输出结果集的一个完整副本,因此查询不会比普通情况下运行得更快,但会生成不正确的结果。相反,计划的并行部分必须是在查询优化器内部称为部分计划的内容;也就是说,它必须构造为执行计划的每个进程都将仅生成输出行的一个子集,即每个所需的输出行都由一个协作进程生成。通常,这意味着查询驱动表的扫描必须是并行感知扫描。

15.3.1. 并行扫描 #

当前支持下列类型的并行感知表扫描。

  • 并行顺序扫描中,表的块将被划分为范围并共享给协作进程。每个工作进程将在请求附加范围的块之前完成其指定块范围的扫描。

  • 并行位图堆扫描中,选择一个进程作为领导程序。该进程对一个或多个索引执行扫描,并建立位图,指出需要访问哪些表块。然后将这些块像在并行顺序扫描中一样在协作进程之间进行划分。换句话说,堆扫描是并行执行的,但底层索引扫描不是。

  • 并行索引扫描并行仅索引扫描中,协作进程轮流从索引中读取数据。当前,仅 btree 索引支持并行索引扫描。每个进程将声明一个索引块,并将扫描并返回该块引用的所有元组;其他进程可以同时从不同的索引块返回元组。并在每个工作进程中以排序的顺序返回并行 btree 扫描的结果。

其他扫描类型(例如,非 btree 索引的扫描)未来可能会支持并行扫描。

15.3.2. 并行连接 #

与非并行计划一样,可以通过嵌套循环、哈希连接或合并连接将驱动表连接到一个或多个其他表。连接的内部可以是任何类型的非并行计划,只要在并行工作程序中运行它是安全的,规划程序也对此提供支持。根据连接类型,内部也可能是并行计划。

  • 嵌套循环连接中,内部始终是非并行的。虽然它被完全执行,但如果内部是一个索引扫描,则这是有效的,因为外部元组和在索引中查找值的循环在协作进程中进行了划分。

  • 合并连接中,内部始终是非并行计划,因此被完全执行。这可能是低效的,尤其是如果必须执行排序,因为工作和结果数据在每个协作进程中都是重复的。

  • 哈希连接(不带“并行”前缀)中,内部由每个协作进程完全执行,以便生成哈希表的相同副本。如果哈希表较大或计划昂贵,这可能效率低下。在 并行哈希连接 中,内部是 并行哈希,它将生成共享哈希表的工作分配给协作进程。

15.3.3. 并行聚合 #

PostgreSQL 通过两阶段聚合支持并行聚合。首先,参与查询并行部分的每个进程执行一个聚合步骤,为该进程所知的每个组生成一个部分结果。这在计划中反映为 部分聚合 节点。其次,将部分结果通过 收集收集合并 传输给领导者。最后,领导者重新聚合所有工作进程中的结果,以便生成最终结果。这在计划中反映为 最终聚合 节点。

由于 最终聚合 节点在领导者进程上运行,所以与输入行数相比生成组数量较多的查询对查询规划器而言效果较差。例如,在最坏的情况下,最终聚合 节点所见的组的数量可能与 部分聚合 阶段所有工作进程所见的输入行数量一样多。对于此类情况,使用并行聚合显然没有性能优势。查询规划器在规划过程中会考虑这一点,并且在这种情况下不太可能选择并行聚合。

并非所有情况下都支持并行聚合。每个聚合对于并行性必须是 安全的,并且必须有一个“组合”函数。如果聚合的转换状态类型为 内部,则它必须具有序列化和反序列化函数。有关更多详细信息,请参见 创建聚合。如果任何聚合函数调用包含 DISTINCTORDER BY 子句,则不支持并行聚合,并且也不支持有序集聚合或查询涉及 GROUPING SETS 的情况。仅当查询中涉及的所有连接也是计划并行部分时,才可以仅使用它。

15.3.4. 并行追加 #

PostgreSQL将来自多个来源的行组合成一个结果集时,它使用AppendMergeAppend计划节点。执行UNION ALL或扫描分区表时,通常会发生这种情况。与在其他计划中一样,可在并行计划中使用此类节点。然而,在并行计划中,计划程序可能会改用Parallel Append节点。

Append节点在并行计划中使用时,每个进程将按子计划出现的顺序执行子计划,以便所有参与进程协作执行第一个子计划,直到其完成,然后在同一时间左右转到第二个计划。如果改用Parallel Append,则执行器会平均地将参与进程分布到其子计划上,以便多个子计划同时执行。这可以避免争用,并避免在从不执行子计划的进程中支付子计划的启动成本。

另外,与在并行计划中使用时仅能使用部分子节点的常规Append节点不同,Parallel Append节点可以使用部分子计划,也可以使用非部分子计划。由于扫描非部分子节点多次将导致重复结果,因此这些节点只能由一个进程扫描。因此,涉及追加多个结果集的计划即使在无法使用有效的局部计划时,也可以实现粗粒度并行性。例如,请考虑一个针对分区表的查询,而该查询只能通过使用不支持并行扫描的索引有效地实现。计划程序可能会选择以常规Index Scan计划的Parallel Append形式实现;每个单独的索引扫描都必须由一个进程执行,但不同的扫描可以由不同的进程同时执行。

enable_parallel_append可用于禁用此功能。

15.3.5. 并行计划提示 #

如果预期会产生并行计划的查询没有生成并行计划,则可以尝试减小parallel_setup_costparallel_tuple_cost。当然,这个计划可能比计划程序首选的串行计划速度慢,但这种情况并不总是会发生。即使将这些设置的值设得非常小(例如,将它们都设为零),仍然没有得到并行计划,则可能是查询计划程序无法为您的查询生成并行计划的某些原因所致。请参阅第 15.2 节第 15.4 节获取关于这可能是何原因的信息。

执行并行计划时,您可以使用 EXPLAIN (ANALYZE, VERBOSE) 对每个计划节点显示每个工作人员的统计信息。这有助于确定工作是否在所有计划节点之间均匀分布,以及更全面地了解计划的性能特性。