1. 引言
根據 Vadim Mikheev 的說法,PostgreSQL 的多版本并發控制(MVCC)是一種“在多用戶環境中提高數據庫性能的高級技術”。該技術要求系統中存在同一數據元組的多個“版本”,這些版本由不同時間段內獲取的快照進行管理。換句話說,在這種技術下,PostgreSQL 需要根據多個參數(如獲取的快照、當前事務 ID 等)來判斷哪些元組對用戶是“可見”的,哪些是不可見的。這也被稱為 PostgreSQL 的“可見性檢查”規則。在本文中,我將討論可見性檢查的基本原理,幫助您了解 PostgreSQL 內部是如何執行這項任務的,希望這對您的開發工作有所幫助。
2. 涉及的參數
- 元組本身,包含以下信息:
- xmin(插入該元組的事務 ID)
- xmax(如果大于 0,表示刪除該元組的事務 ID;否則表示未被刪除)
- cid(命令 ID)
- hintbit(提示位)
- 全局頂級事務 ID(如果存在)
- 當前快照,包含 xmin、xmax 和 cid
- 提交日志數據(CLOG)
3. 檢查過程
3.1 檢查提示位(Hintbit)
可見性檢查過程從檢查提示位(hintbit)開始。如果提示位的狀態為“已提交”(COMMITTED),則可以跳過大部分其他可見性檢查規則以提高效率。對于已提交的元組,PostgreSQL 會獲取該元組的 xmin 值,并與當前快照進行比較,以確保該元組當前未處于“進行中”(in progress)狀態(參見 3.3 節中的公式以確定“進行中”狀態)。這一檢查是必要的,因為即使元組已提交,仍有可能被其他后臺進程在此時更新。如果元組未被其他后臺進程“進行中”,則最后會檢查其 xmax 值以確保它是無效的(即未被刪除)。當上述條件都滿足時,該元組被認為是對用戶可見的。
如果提示位顯示“已中止”(ABORTED),則該元組對用戶不可見。如果提示位沒有值,則 PostgreSQL 會繼續執行后續的可見性檢查規則。
3.2 檢查元組的 xmin 是否等于全局頂級事務 ID
下一個檢查步驟是將元組的 xmin 與全局頂級事務 ID 進行比較,看它們是否相等。全局頂級事務 ID 僅在用戶通過 BEGIN
語句手動啟動事務時才會被設置。如果用戶未以這種方式啟動事務(即未發出 BEGIN
語句),則全局頂級事務 ID 不會被設置,因此此檢查將被跳過。
如果元組的 xmin 等于全局頂級事務 ID,則意味著該元組當前由當前后臺進程(而非其他進程)“進行中”。此時,命令 ID(cid)將用于確定可見性。在事務塊中,發出的每個命令都有一個關聯的命令 ID,用于指示命令的先后順序。例如,如果在一個事務中,SELECT
命令在 UPDATE
命令之后,那么 SELECT
必須看到由之前的 UPDATE
命令更新的新元組,cid 在這一確定中起作用。這種行為還受“隔離級別”(isolation level)的影響,但隔離級別不在本文討論范圍內。默認的隔離級別是“讀已提交”(READ COMMITTED),這使得 SELECT
能看到由之前的 UPDATE
更改的數據;但如果隔離級別設置為“可重復讀”(REPEATABLE READ),則 SELECT
不會看到由之前的 UPDATE
更改的數據。請牢記這一點。
如果元組的 xmin 不等于全局頂級事務 ID,則 PostgreSQL 會繼續執行后續的可見性檢查規則。
3.3 檢查當前快照
下一個檢查步驟是 PostgreSQL 檢查該元組是否被其他后臺進程“進行中”。這是通過將元組的 xmin 值與當前快照的 xmin 和 xmax 值進行比較來完成的,公式如下:
- 元組 xmin < 快照 xmin:表示未進行中
- 元組 xmin >= 快照 xmax:表示進行中
如果根據快照判斷該元組為“進行中”,則該元組對當前用戶不可見,因為其他后臺進程仍在處理它。如果元組未被認為“進行中”,則 PostgreSQL 會繼續執行后續的可見性檢查規則。
3.4 檢查提交日志(CLOG)
下一個檢查步驟是 PostgreSQL 獲取元組的 xmin 值,并對照提交日志(CLOG)檢查該元組是否已提交或中止。CLOG 類似于一個事務 ID 數組,每個數組元素存儲一個提交狀態。PostgreSQL 使用公式將事務 ID 轉換為 CLOG 塊加上偏移量,以精確訪問正確的 CLOG 元素。CLOG 數據結構會通過檢查點(checkpoint)進程定期刷新到磁盤,存儲在以下三個目錄中:pg_xact
、pg_multixact
和 pg_subtrans
。
如果 CLOG 顯示該元組已提交,PostgreSQL 會繼續檢查元組的 xmax 值以確保它是無效的(即未被刪除)。如果 xmax 無效,則該元組對用戶可見。同時,PostgreSQL 會將該元組的提示位更新為“已提交”(COMMITTED),以便在下次可見性檢查時無需再次訪問 CLOG,因為訪問 CLOG 的成本較高。
如果 CLOG 顯示該元組已中止或無效,則該元組對當前用戶不可見。同時,PostgreSQL 會將該元組的提示位更新為“無效”(INVALID)。
4. 總結
以上是我對 PostgreSQL 中基本可見性規則的理解。當然,還有其他復雜的檢查未在此提及,例如子事務和多事務的處理,但它們的模式與本文提到的內容有些相似。