如果 FDW 的底層儲存機制具有鎖定單個行以防止併發更新的機制,那麼對於 FDW 來說,進行行級鎖定通常是值得的,並且應儘可能接近普通 PostgreSQL 表的語義。這涉及到多個考慮因素。
需要做出的一個關鍵決定是執行“早期鎖定”還是“晚期鎖定”。在早期鎖定中,當行第一次從底層儲存檢索時就會被鎖定,而在晚期鎖定中,只有當已知需要鎖定該行時才鎖定它。(區別在於一些行可能會被本地檢查的限制或連線條件丟棄。)早期鎖定更簡單,並且可以避免額外的與遠端儲存的往返通訊,但它可能導致鎖定不需要鎖定的行,從而降低併發性甚至導致意外的死鎖。此外,晚期鎖定僅在可以稍後唯一地重新識別要鎖定的行時才可能。最好是行識別符號能識別行的特定版本,就像 PostgreSQL 的 TID 那樣。
預設情況下,PostgreSQL 在與 FDW 互動時會忽略鎖定方面的考慮,但 FDW 可以在沒有任何核心程式碼顯式支援的情況下執行早期鎖定。在 PostgreSQL 9.5 中新增的 第 58.2.6 節 中描述的 API 函式允許 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
的元組來執行早期鎖定。要改為執行晚期鎖定,請提供 第 58.2.6 節 中定義的 callback 函式。在 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
來覆蓋預設的複製整行資料的選擇。這將導致 RefetchForeignRow
使用 markType
的該值被呼叫;然後它應該在不獲取任何新鎖的情況下重新獲取行。(如果您有一個 GetForeignRowMarkType
函式但不想重新獲取未鎖定的行,請為 LCS_NONE
選擇 ROW_MARK_COPY
選項。)
有關更多資訊,請參閱 src/include/nodes/lockoptions.h
、src/include/nodes/plannodes.h
中 RowMarkType
和 PlanRowMark
的註釋,以及 src/include/nodes/execnodes.h
中 ExecRowMark
的註釋。
如果您在文件中發現任何不正確之處、與您對特定功能的實際體驗不符或需要進一步澄清的地方,請使用 此表格 來報告文件問題。