规划器将查询中涉及的操作分类为并行安全、
并行受限或并行不安全。
并行安全的操作是指不会与并行查询的使用产生冲突的操作。
并行受限的操作是指不能在并行工作者中执行,但可以在并行查询
的领导者中执行。因此,并行受限的操作不能出现在Gather
或Gather Merge节点之下,但可以出现在包含这类节点的
计划的其他位置。并行不安全的操作是指在并行查询使用时无法执行的操作,
甚至在领导者中也不能执行。当一个查询包含任何并行不安全的内容时,
并行查询将完全禁用该查询。
下面的操作总是并行受限:
公共表表达式(CTE)的扫描。
临时表的扫描。
外部表的扫描,除非外部数据包装器有一个IsForeignScanParallelSafe API
指示其他情况。
引用一个相关的SubPlan的计划节点。
规划器无法自动判定一个用户定义的函数或者聚合是并行安全、并行受限还是并行
不安全,因为这需要预测函数可能执行的每一个操作。一般而言,这就相当于一个
停机问题,因此是不可能的。甚至对于可以做到判定的简单函数我们也不会尝试,
因为那会非常昂贵而且容易出错。相反,除非是被标记出来,所有用户定义的函数
都被认为是并行不安全的。在使用CREATE FUNCTION或者
ALTER FUNCTION时,可以通过指定PARALLEL SAFE、
PARALLEL RESTRICTED或者PARALLEL UNSAFE来设置标记。
在使用CREATE AGGREGATE时,PARALLEL选项可以被
指定为SAFE、RESTRICTED或者UNSAFE。
如果函数和聚合写入数据库、更改事务状态(除通过子事务进行错误恢复外)、访问序列
或对设置进行持久更改,则必须标记为PARALLEL UNSAFE。同样,如果函数
访问临时表、客户端连接状态、游标、预处理语句或系统无法在工作进程间同步的其他
后端本地状态,则必须标记为PARALLEL RESTRICTED。例如,
setseed和random正是出于这个原因被标记为并行受限。
一般而言,如果一个函数是受限或者不安全的却被标记为安全,或者它实际是不安全的
却被标记为受限,把它用在并行查询中时可能会抛出错误或者产生错误的回答。如果
C 语言函数被错误标记,理论上它会展现出完全不明确的行为,因为系统中无法保护
自身不受任意 C 代码的影响。但是,在最有可能的情况下,结果不会比其他任何
函数更糟糕。如果有疑虑,最好还是标记函数为UNSAFE。
如果在并行工作者中执行的函数要求领导者没有持有的锁,例如读该查询中没有引用
的表,那么工作者退出时会释放那些锁(而不是在事务结束时释放)。如果你写了一个
这样做的函数并且这种不同的行为对你很重要,把这类函数标记为PARALLEL
RESTRICTED以确保它们只在领导者中执行。
注意查询规划器不会为了获取一个更好的计划而考虑延迟计算并行受限的函数或者聚集。
所以,如果一个被应用到特定表的WHERE子句是并行受限的,查询
规划器就不会考虑对处于计划并行部分的表执行一次扫描。在一些情况中,可以(甚至
效率更高)把对表的扫描包括在查询的并行部分并且延迟对WHERE子句
的计算,这样它会出现在Gather节点之上。不过,规划器不会这样做。