规划器会将查询中涉及的操作分类为并行安全、并行受限或并行不安全。并行安全的操作不会与并行查询的使用产生冲突。并行受限的操作不能在并行工作进程中执行,但可以在启用并行查询时由领导者执行。因此,并行受限的操作绝不能出现在 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 节点之上,可能是可行的,甚至更高效。然而,规划器不会这样做。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。