跳轉到內容

PostgreSQL/MVCC

來自華夏公益教科書


在幾乎所有情況下,PostgreSQL 資料庫必須支援許多客戶端,這些客戶端希望同時新增或更改資料。 這使得有必要保護併發執行的請求彼此不受影響 - 最好是不用阻塞它們。 可能會出現兩種情況:兩個客戶端想要同時更改同一行,或者一個客戶端想要撤銷(回滾)他的更改,而另一個客戶端可能已經嘗試讀取最新版本。

想象一下,一個線上商店提供最後一款商品。 兩個客戶端在他們的使用者介面上顯示該商品。 一段時間後,但同時,兩個客戶端都決定將該商品放入他們的購物車,甚至購買它。 兩個人都看到了這件商品,但只允許其中一個人購買。 資料庫必須強制執行請求的順序,允許其中一個寫入訪問,阻止另一個寫入,並通知被阻止的客戶端資料已由其他程序更改,應重新讀取。

PostgreSQL 實現了一種複雜的技術來處理併發訪問,避免鎖定:多版本併發控制 (MVCC)。 與鎖定一行不同,MVCC 技術在資料更改發生時建立該行的最新版本。 “使用 MVCC... 而不是鎖定的主要優勢在於,在 MVCC 中,用於查詢(讀取)資料的鎖不會與用於寫入資料的鎖衝突,因此讀取永遠不會阻塞寫入,而寫入永遠不會阻塞讀取。 PostgreSQL 即使在透過使用... 可序列化快照隔離 (SSI) 提供最嚴格的交易隔離級別時,也保持此保證”。 [1]

MVCC 的實現基於事務 ID (XID)。 叢集中的每個事務都會獲得一個唯一的順序編號作為其 ID。 每個 INSERTUPDATEDELETE 命令都會將 XID 儲存在受影響行的 xminxmax 中。 xminxmax 和一些其他系統列包含在每行中。 這兩個列通常使用 SELECT * FROM ... 命令不可見。 但可以使用 SELECT xmin, xmax, * FROM ... 之類的命令讀取它們。 xmin 列包含建立此行版本的交易的 XID,xmax 列包含刪除此版本的交易的 XID,或者如果該版本未被刪除,則為零。

那麼,當寫入訪問發生時,具體發生了什麼呢? 下面的圖形顯示了關於 xminxmax 和常規應用程式資料的詳細資訊。

INSERT 命令建立行的第一個版本。 除了其應用程式資料“x”之外,此版本還包含建立交易 123 的 ID 在 xmin 中,而 0 在 xmax 中。 xmin 表示該版本自交易 123 以來存在,而 xmax 中的值 0 表示它當前未被刪除。

稍後,交易 135 透過將應用程式資料從“x”更改為“y”來執行此行的 UPDATE。 根據 MVCC 原則,行舊版本的 data 不會更改。 值“x”保持原樣。 只有 xmax 會更改為 135。 現在,此版本被視為僅對 XID 從 123 到 134 的交易有效。 除了保留舊版本的 data 外,UPDATE 還使用其 XID 在 xmin 中、0 在 xmax 中和“y”在應用程式 data 中 (以及舊版本中的所有其他應用程式 data) 建立完整行的最新版本。 此新行版本對所有將來的交易可見。 (在內部,UPDATE 命令充當 DELETE 命令,後跟 INSERT 命令。)

所有後續的 UPDATE 命令的行為與第一個命令相同:它們將其 XID 放入當前版本的 xmax 中,並使用其 XID 在 xmin 中和 0 在 xmax 中建立最新版本。

最後,DELETE 命令可能會刪除一行。 即使在這種情況下,包括最新版本在內的行的所有版本都保留在資料庫中 - 不會丟棄任何東西。 只有最後一個版本的 xmax 設定為 DELETE 交易的 XID,這表示它僅對 XID 較早的交易可見 - 在此示例中,從 142 到 820。

總之,MVCC 技術在表的堆檔案中建立了越來越多相同行的版本,並將它們保留在那裡,即使在 DELETE 命令之後也是如此。 只有最年輕的版本對所有未來的交易相關。 但該系統還必須在短時間內保留一些舊版本,因為它們可能仍然被早於刪除交易並因此具有較小 XID 的交易請求。 隨著時間的推移,即使是最舊的版本也對所有交易變得無關緊要,因此最終變得不再需要。 然而,它們確實物理地存在於磁碟上並佔用空間。 它們被稱為 死行,是所謂 膨脹 的一部分。

請記住

  • xminxmax 指示交易可見行版本的範圍。 此範圍不暗示任何直接的時間含義。 XID 的順序僅反映交易開始事件的順序。
  • 在內部,UPDATE 命令的行為與 DELETE 命令,後跟 INSERT 命令相同。
  • 不會刪除任何內容 - 這會導致資料庫佔用越來越多的磁碟空間。 很明顯,這種行為必須以某種方式得到糾正。 下一章將解釋 vacuumautovacuum 如何完成此任務。

到目前為止,這僅僅是對 MVCC 原理的粗略描述。 該實現考慮了更多問題,例如

  • ROLLBACK 命令可以撤銷更改。
  • 一段時間後,XID 序列可能會從零開始 (迴圈)。 在這種情況下,xmax 可能小於 xmin

XID 是序列 (在 9.4 之前的 PostgreSQL 版本中,有一個保留值來處理 迴圈)。 PostgreSQL 瞭解一些與事務及其 XID 相關的配置引數,其名稱類似於 xxx_age,例如:vacuum_freeze_min_age。 對於此類引數,“年齡”不指定時間段,而是表示一定數量的事務,例如,1 億。

參考資料

[編輯 | 編輯原始碼]


華夏公益教科書