规划器把查询中涉及的操作分类成并行安全、并行受限
或者并行不安全。并行安全的操作不会与并行查询的使用产生冲突。
并行受限的操作不能在并行工作者中执行,但是能够在并行查询的领导者中执行。
因此,并行受限的操作不能出现在Gather
或Gather Merge
节点之下,
但是能够出现在包含有这样节点的计划的其他位置。并行不安全的操作不能在并行查询中执行,甚至不能在领导者中执行。当一个查询包含任何并行不安全操作时,并行查询对这个查询是完全被禁用的。
下面的操作总是并行受限的。
公共表表达式(CTE)的扫描。
临时表的扫描。
外部表的扫描,除非外部数据包装器有一个IsForeignScanParallelSafe
API。
对InitPlan
或者相关的SubPlan
的访问。
规划器无法自动判定一个用户定义的函数或者聚合是并行安全、并行受限还是并行不安全,因为这需要预测函数可能执行的每一个操作。一般而言,这就相当于一个停机问题,因此是不可能的。甚至对于可以做到判定的简单函数我们也不会尝试,因为那会非常昂贵而且容易出错。相反,除非是被标记出来,所有用户定义的函数都被认为是并行不安全的。在使用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
节点之上。不过,规划器不会这样做。