2024年9月26日: PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:开发
不受支持的版本:11 / 10 / 9.6 / 9.5

57.5. 外部数据包装器中的行级锁定 #

如果 FDW 的底层存储机制具有锁定单个行的概念,以防止对这些行的并发更新,则 FDW 通常值得执行行级锁定,并尽可能地接近普通 PostgreSQL 表中使用的语义。这涉及多个因素。

需要做出的一项关键决定是执行早期锁定还是后期锁定。在早期锁定中,在首次从底层存储中检索行时锁定该行,而在后期锁定中,仅在已知需要锁定该行时才锁定该行。(区别在于,某些行可能会被本地检查的限制或连接条件丢弃。)早期锁定更简单,避免了额外的往返远程存储的行程,但它会导致对不需要锁定的行的锁定,从而导致并发性降低,甚至出现意外死锁。此外,只有当要锁定的行以后可以被唯一地重新识别时,后期锁定才可能实现。最好是行标识符应该识别行的特定版本,就像 PostgreSQL TIDs 一样。

默认情况下,PostgreSQL 在与 FDW 交互时会忽略锁定考虑因素,但 FDW 可以执行早期锁定,而无需核心代码的任何显式支持。在 PostgreSQL 9.5 中添加的 第 57.2.6 节 中描述的 API 函数允许 FDW 使用后期锁定,如果需要的话。

另一个考虑因素是,在 READ COMMITTED 隔离级别下,PostgreSQL 可能需要针对某些目标元组的更新版本重新检查限制和连接条件。重新检查连接条件需要重新获取以前与目标元组连接的非目标行的副本。在处理标准 PostgreSQL 表时,这是通过将非目标表的 TIDs 包含在通过连接投影的列列表中来完成的,然后在需要时重新获取非目标行。这种方法使连接数据集保持紧凑,但它需要廉价的重新获取功能,以及可以唯一标识要重新获取的行版本的 TID。因此,默认情况下,针对外部表使用的方法是,将从外部表获取的整行副本包含在通过连接投影的列列表中。这不会对 FDW 提出特殊要求,但会导致合并连接和哈希连接的性能降低。能够满足重新获取要求的 FDW 可以选择以第一种方式执行。

对于外部表的 UPDATEDELETE 操作,建议目标表的 ForeignScan 操作对它获取的行执行早期锁定,可能是通过等效于 SELECT FOR UPDATE 的方式。FDW 可以通过将它的 relid 与 root->parse->resultRelation 进行比较,在计划时间检测到一个表是否是 UPDATE/DELETE 目标,或者在执行时间使用 ExecRelationIsTargetRelation() 检测。另一种可能性是在 ExecForeignUpdateExecForeignDelete 回调中执行后期锁定,但没有提供对此的特殊支持。

对于指定要由 SELECT FOR UPDATE/SHARE 命令锁定的外部表,ForeignScan 操作可以通过使用等效于 SELECT FOR UPDATE/SHARE 的方式获取元组来再次执行早期锁定。要执行后期锁定,请提供 第 57.2.6 节 中定义的回调函数。在 GetForeignRowMarkType 中,根据请求的锁定强度选择行标记选项 ROW_MARK_EXCLUSIVEROW_MARK_NOKEYEXCLUSIVEROW_MARK_SHAREROW_MARK_KEYSHARE。(无论您选择哪一种这四种选项,核心代码的行为都将相同。)在其他地方,您可以通过在计划时间使用 get_plan_rowmark,或者在执行时间使用 ExecFindRowMark 来检测是否指定了对外部表的这种类型的命令进行锁定;您不仅要检查是否返回了非空的 rowmark 结构,还要检查它的 strength 字段是否不是 LCS_NONE

最后,对于在 UPDATEDELETESELECT FOR UPDATE/SHARE 命令中使用但未指定为行级锁定的外部表,您可以通过让 GetForeignRowMarkType 在看到锁定强度为 LCS_NONE 时选择选项 ROW_MARK_REFERENCE 来覆盖复制整行的默认选择。这将导致 RefetchForeignRow 被调用,并将 markType 的值用于它;然后它应该重新获取该行,而无需获取任何新的锁定。(如果您有 GetForeignRowMarkType 函数,但不想重新获取未锁定的行,则为 LCS_NONE 选择选项 ROW_MARK_COPY。)

有关更多信息,请参阅 src/include/nodes/lockoptions.hsrc/include/nodes/plannodes.hRowMarkTypePlanRowMark 的注释,以及 src/include/nodes/execnodes.hExecRowMark 的注释。

提交更正

如果您在文档中看到任何不正确的内容,不符合您对特定功能的体验,或者需要进一步澄清,请使用 此表单 报告文档问题。