如果 FDW 的底层存储机制具有锁定单个行的概念,以防止对这些行进行并发更新,那么通常值得 FDW 执行行级锁定,使其尽可能接近普通 PostgreSQL 表中使用的语义。这涉及多个方面的考虑。
需要做出的一个关键决定是执行早期锁定还是后期锁定。在早期锁定中,当从底层存储首次检索到行时,该行会被锁定;而在后期锁定中,仅当已知需要锁定行时,该行才会被锁定。(之所以存在差异,是因为某些行可能会被本地检查的限制或连接条件丢弃。)早期锁定要简单得多,并且避免了到远程存储的额外往返,但是它可能会导致锁定不需要锁定的行,从而导致并发性降低,甚至出现意外的死锁。另外,只有当以后可以唯一地重新识别要锁定的行时,才有可能进行后期锁定。最好是行标识符应标识行的特定版本,就像 PostgreSQL TID 所做的那样。
默认情况下,PostgreSQL 在与 FDW 接口时会忽略锁定方面的考虑,但是 FDW 可以执行早期锁定,而无需核心代码的任何显式支持。在第 57.2.6 节中描述的 API 函数(在 PostgreSQL 9.5 中添加)允许 FDW 如果愿意使用后期锁定。
另一个需要考虑的因素是,在 READ COMMITTED
隔离模式下,PostgreSQL 可能需要针对某些目标元组的更新版本重新检查限制和连接条件。重新检查连接条件需要重新获取先前与目标元组连接的非目标行的副本。当使用标准 PostgreSQL 表时,这是通过在通过连接投影的列列表中包含非目标表的 TID,然后在需要时重新获取非目标行来完成的。这种方法使连接数据集保持紧凑,但是它需要廉价的重新获取能力,以及可以唯一标识要重新获取的行版本的 TID。因此,默认情况下,用于外部表的方法是在通过连接投影的列列表中包含从外部表获取的整个行的副本。这不会对 FDW 提出特殊要求,但可能会导致合并和哈希连接的性能降低。能够满足重新获取要求的 FDW 可以选择使用第一种方法。
对于外部表的 UPDATE
或 DELETE
,建议对目标表执行 ForeignScan
操作,对其获取的行执行早期锁定,可能通过类似于 SELECT FOR UPDATE
的方式。FDW 可以通过将其 relid 与 root->parse->resultRelation
进行比较,在计划时检测表是否为 UPDATE
/DELETE
目标,或者在执行时使用 ExecRelationIsTargetRelation()
。另一种可能性是在 ExecForeignUpdate
或 ExecForeignDelete
回调中执行后期锁定,但是没有为此提供特殊支持。
对于指定要由 SELECT FOR UPDATE/SHARE
命令锁定的外部表,ForeignScan
操作可以再次通过使用类似于 SELECT FOR UPDATE/SHARE
的方式获取元组来执行早期锁定。要改为执行后期锁定,请提供在第 57.2.6 节中定义的回调函数。在 GetForeignRowMarkType
中,根据请求的锁定强度,选择行标记选项 ROW_MARK_EXCLUSIVE
、ROW_MARK_NOKEYEXCLUSIVE
、ROW_MARK_SHARE
或 ROW_MARK_KEYSHARE
。(核心代码将执行相同的操作,而与您选择这四个选项中的哪一个无关。)在其他地方,您可以通过在计划时使用 get_plan_rowmark
,或者在执行时使用 ExecFindRowMark
来检测是否已指定通过此类命令锁定外部表;您不仅必须检查是否返回了非空 rowmark 结构,而且必须检查其 strength
字段是否不是 LCS_NONE
。
最后,对于在 UPDATE
、DELETE
或 SELECT FOR UPDATE/SHARE
命令中使用,但未指定要进行行锁定的外部表,您可以通过使 GetForeignRowMarkType
在看到锁定强度 LCS_NONE
时选择选项 ROW_MARK_REFERENCE
来覆盖复制整个行的默认选择。这将导致使用该 markType
值调用 RefetchForeignRow
;然后它应重新获取该行,而无需获取任何新锁。(如果您具有 GetForeignRowMarkType
函数,但不希望重新获取未锁定的行,请为 LCS_NONE
选择选项 ROW_MARK_COPY
。)
有关其他信息,请参见 src/include/nodes/lockoptions.h
,src/include/nodes/plannodes.h
中 RowMarkType
和 PlanRowMark
的注释,以及 src/include/nodes/execnodes.h
中 ExecRowMark
的注释。
如果您在文档中看到任何不正确、与您使用特定功能的经验不符或需要进一步澄清的内容,请使用 此表单来报告文档问题。