15.4. 并行安全性#
规划器将查询中涉及的操作分类为并行安全、并行受限或并行不安全。并行安全操作不会与并行查询的使用产生冲突。并行受限操作无法在并行工作程序中执行,但可以在使用并行查询时在领导者中执行。因此,并行受限操作永远不会出现在Gather
或Gather Merge
节点下方,但可能出现在包含此类节点的计划中的其他位置。并行不安全操作无法在使用并行查询时执行,即使在领导者中也不行。当查询包含任何并行不安全的内容时,将为该查询完全禁用并行查询。
以下操作始终是并行受限的
公共表表达式 (CTE) 的扫描。
临时表的扫描。
外部表的扫描,除非外部数据包装器具有指示相反情况的
IsForeignScanParallelSafe
API。附加了
InitPlan
的计划节点。引用相关
SubPlan
的计划节点。
15.4.1. 函数和聚合的并行标记#
规划器无法自动确定用户定义的函数或聚合是并行安全的、并行受限的还是并行不安全的,因为这需要预测函数可能执行的每个操作。通常,这等同于停机问题,因此是不可能的。即使对于可以想象完成的简单函数,我们也不会尝试,因为这既昂贵又容易出错。相反,除非另有标记,否则所有用户定义的函数都假定为并行不安全。使用CREATE FUNCTION或ALTER FUNCTION时,可以通过指定PARALLEL SAFE
、PARALLEL RESTRICTED
或PARALLEL UNSAFE
(视情况而定)来设置标记。使用CREATE AGGREGATE时,可以将PARALLEL
选项指定为SAFE
、RESTRICTED
或UNSAFE
作为相应的值。
如果函数和聚合写入数据库、访问序列、更改事务状态(即使是临时状态)(例如,建立EXCEPTION
块来捕获错误的 PL/pgSQL 函数)或对设置进行持久性更改,则必须将它们标记为PARALLEL UNSAFE
。类似地,如果函数访问临时表、客户端连接状态、游标、已准备好的语句或系统无法跨工作程序同步的其他后端本地状态,则必须将它们标记为PARALLEL RESTRICTED
。例如,setseed
和random
由于最后这个原因而受到并行限制。
一般来说,如果一个函数被标记为在受限或不安全时是安全的,或者如果它被标记为在实际上不安全时是受限的,那么在并行查询中使用它时可能会引发错误或产生错误答案。C 语言函数在被错误标记时理论上可能会表现出完全未定义的行为,因为系统无法保护自己免受任意 C 代码的侵害,但在大多数情况下,结果不会比任何其他函数更糟。如有疑问,最好将函数标记为UNSAFE
。
如果在并行工作进程中执行的函数获取了领导者未持有的锁,例如通过查询查询中未引用的表,那么这些锁将在工作进程退出时释放,而不是在事务结束时释放。如果您编写了执行此操作的函数,并且此行为差异对您很重要,请将此类函数标记为PARALLEL RESTRICTED
以确保它们仅在领导者中执行。
请注意,查询计划程序不会考虑延迟评估查询中涉及的并行受限函数或聚合以获取更好的计划。因此,例如,如果应用于特定表的WHERE
子句是并行受限的,那么查询计划程序将不会考虑在计划的并行部分中扫描该表。在某些情况下,可以(甚至可能很有效)将该表的扫描包括在查询的并行部分中,并延迟评估WHERE
子句,以便它在Gather
节点之上发生。但是,计划程序不会这样做。