PostgreSQL 9.5.3 中文手册 | |||
---|---|---|---|
上一页 | 上一级 | 章 54. 编写一个外部数据包装器 | 下一页 |
如果FDW的底层存储机制具有锁定独立行的概念用来防止这些行的并发更新, FDW执行行级锁尽可能近似实际在普通PostgreSQL表的语义是值得的。 有涉及这个的多方面考虑。
关键决定是是否执行早期锁定或者后期锁定。 早期锁定中,当开始从底层存储中检索的时候,行是被锁定的,而在后期锁定中, 当它是已知的需要被锁定的时候,该行才被锁定。 (产生差异,因为局部检查限制或连接条件某些行可能会被丢弃。) 早期锁定更简单,并且避免了到远程存储的额外往返, 但它可以导致不需要被锁定的行锁定,从而减少了并发甚至意外死锁。 此外,如果要锁定的行可以后期被唯一地重新确定,那么后期锁定是唯一可能。 优选行标识符标识行的特定版本,正如PostgreSQL TID一样。
默认情况下,当连接到FDW的时候,PostgreSQL忽略锁定因素, 但FDW可提早进行锁定而无核心代码的任何明确支持。 第 54.2.4 节中描述的API函数, 被添加到PostgreSQL 9.5中,如果愿意允许FDW使用原来的锁定。
另一个考虑是在READ COMMITTED隔离模式中, PostgreSQL可能需要重新检查一些目标元组更新版本的限制和链接条件。 当执行标准PostgreSQL表时, 重新检查链接条件需要重新获取之前加入到目标元组的非目标行的副本。 通过包含连接列表中非目标表的TID来执行, 当需要时,然后重新获取非目标行。 这种方法保持连接数据集的紧凑,但它需要廉价的重新获取能力, 以及可以唯一标识被重新取出的行版本的TID。 默认情况下,因此,使用外部表的方法是包括通过链接列表中的外部表抓取的整行的副本。 这对FDW并没有特别的要求,但导致合并以及哈希连接的性能降低。 能够满足重新提取需求的FDW可以选择第一种方式执行它。
对于外表上UPDATE或者DELETE,
推荐目标表上的ForeignScan操作在抓取的行上执行早期锁定,
可能相当于SELECT FOR UPDATE。
FDW可以在计划时间内通过比较relid到root->parse->resultRelation
或者在执行时间通过使用ExecRelationIsTargetRelation()
检测
表是否是UPDATE/DELETE目标。
另一种可能性是在ExecForeignUpdate
或者ExecForeignDelete
回调中
执行后期锁定,但没有这个特别的支持。
指定外部表通过SELECT FOR UPDATE/SHARE命令锁定,
ForeignScan操作可以通过抓取使用等效SELECT FOR UPDATE/SHARE元组执行早期锁定。
相反执行后期锁定,提供第 54.2.4 节里面定义的回调函数。
在GetForeignRowMarkType
中,
选择rowmark选项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命令中使用的外部表,
但未指定行锁定,当看到锁强度LCS_NONE时,你可以通过GetForeignRowMarkType
选择选项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注释 获取更多信息。