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

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 通过分两阶段进行聚合来支持并行聚合。首先,参与查询并行部分的每个进程执行一个聚合步骤,为该进程所感知的每个组生成一个部分结果。这在计划中反映为 Partial Aggregate 节点。其次,部分结果通过 GatherGather Merge 传输给领导者。最后,领导者重新聚合所有工作进程中的结果以生成最终结果。这在计划中反映为 Finalize Aggregate 节点。

由于 Finalize Aggregate 节点在领导者进程上运行,因此与输入行数相比生成相对较多组的查询对查询计划器来说似乎不太有利。例如,在最坏的情况下,Finalize Aggregate 节点看到的组数可能与 Partial Aggregate 阶段所有工作进程看到的输入行数一样多。对于这种情况,显然使用并行聚合不会带来性能优势。查询计划器在计划过程中会考虑这一点,并且在这种情况下不太可能选择并行聚合。

并非所有情况下都支持并行聚合。每个聚合都必须对并行安全,并且必须具有组合函数。如果聚合具有类型为 internal 的转换状态,则它必须具有序列化和反序列化函数。有关更多详细信息,请参阅 CREATE AGGREGATE。如果任何聚合函数调用包含 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) 为每个计划节点显示每个工作进程的统计信息。这可能有助于确定工作是否在所有计划节点之间均匀分布,并且更普遍地了解计划的性能特征。