PgSQL · 內核解析 · 同步流複製實現分析

摘要: PostgreSQL 的流複製自引入以來以穩定著稱,近幾年的幾個大版本陸續完成了好幾個大特性,例如 遠程物理備份 同步流複製 級聯流複製 邏輯流複製 讓流複製在整個 PostgreSQL 技術方案中扮演越來越重要的角色。

原文

PostgreSQL 的流複製自引入以來以穩定著稱,近幾年的幾個大版本陸續完成了好幾個大特性,例如

  1. 遠程物理備份
  2. 同步流複製
  3. 級聯流複製
  4. 邏輯流複製

讓流複製在整個 PostgreSQL 技術方案中扮演越來越重要的角色。 本文將剖析 PostgreSQL 同步流複製的關鍵實現細節,希望大家喜歡。

相關概念

一.物理流複製

物理流複製是一種資料庫主備同步技術,該特性同步的數據是資料庫中物理頁面變化數據(WAL),該模式備庫的底層數據頁面狀態和主庫完全相同,這樣的實現方案讓資料庫主備以及同步狀態都非常穩定。

二.流複製中的角色

  • 主庫 backend 進程,它負責執行用戶的 SQL,在修改數據前會先記錄 WAL(Write-Ahead Logging)日誌。這些日誌中事物提交日誌(CommitTransaction)由 backend 進程負責寫到磁碟。
  • 主庫 WALsender 進程,負責把 WAL 日誌發送給備庫的 WALreceiver 進程。
  • 備庫 WALreceiver 進程,負責接收 WALsender 發送的 WAL 日誌,並持久化到存儲。
  • 備庫 startup 進程,負責恢復 WALsender 寫到磁碟上的 WAL 日誌,把數據 apply 到數據頁面上。

非同步流複製和同步流複製

一.非同步流複製

默認狀態下的流複製是以非同步方式工作的,也就是說主庫寫本地數據和 WAL 日誌,WALsender 非同步的把數據發送給備庫,備庫收到數據後再非同步的做數據恢復。

非同步模式可以做到較好的性能,它的劣勢是:極端情況下,主庫如果當機,被庫被激活成主庫,部分 WAL 沒有發送到備庫,可能造成數據丟失。

二.同步流複製

相對於非同步模式,PostgreSQL 還支持同步模式的流複製。同模模式可以細分為三級

  • REMOTE_WRITE 保證該事務的所有數據被備庫收到(備庫收到數據並調用 write 寫磁碟,但並未持久化到磁碟)
  • REMOTE_FLUSH 保證該事務的所有數據在備庫持久化到磁碟(調用 flush,但只讀查詢看不到)
  • REMOTE_APPLY 保證該事務的所有數據在備庫被恢復到數據頁面(恢復進程讀取並解析 WAL,再 APPLY 到數據頁面,在備庫上執行的只讀查詢能看到數據的變化)

三. 同步流複製源碼解析

1. MVCC 機制和數據可見性

簡單的說 PostgreSQL ACID 是基於 MVCC 和 WAL 技術。數據的修改過程可以簡單描述為

  • 首先 backend 開啟是一個事務,獲得一個事務號 XID;
  • 在這個事務中對數據的任意修改,都被 XID 標記。
  • 其他 backend 在掃描數據時,會看到被這個 XID 修改過的數據,根據當前的隔離級別,選擇對這些數據是否可見(默認的讀已提交隔離級別看不到這些數據)。
  • 只有當此 XID 最後被標記成 commit (寫 WAL commit log 和寫 clog)後,其他的 backend 才能看到這個 XID 修改的數據。

2. 同模流複製的關鍵點

總結一下,實現流複製的同步模式,關鍵點在每個事務提交或回滾時,保證它產生的所有數據變化日誌,即 WAL 都「同步」到備庫。最後一條 WAL commit log 尤為關鍵。

3. 如何實現同步流複製

鋪墊完所有概念和前提技術,我們看看同步模式具體是怎麼實現的。 以事務提交流程為例:

  • [主庫 backend 進程]調用 RecordTransactionCommit 中寫 WAL commit log,獲得這條日誌在在 WAL 中的位置 XLogRecPtr
  • [主庫 backend 進程]完成寫 WAL 後,進入 SyncRepWaitForLSN 等待 WAL 日誌「同步」到備庫。具體做法是:在共享內存中創建一個等待隊列 SHMQueue 記錄 XLogRecPtr,並調動 WaitLatch,讓出 CPU 等待被喚醒。
  • [主庫 WALsender 進程]相應所有備庫的 WALreceiver 拉取 WAL 的請求。把 WAL 發送給所有備庫。
  • [備庫 WALreceiver 進程]寫 WAL 的偏移(LogstreamResult.Write)和持久化 WAL 偏移(LogstreamResult.Flush)記錄下來。
  • [備庫 startup 進程]不斷的恢複數據,把當前恢復到的 WAL 位點放在共享內存 xlogctl->lastReplayedEndRecPtr 中。
  • [備庫 WALreceiver 進程]不斷通過 r 報文和主庫 WALsender 進程同步的狀態,即 XLOG_WRITE_LSN XLOG_REMOTE_LSN XLOG_APPLY_LSN(XLogWalRcvSendReply)
  • [主庫 WALsender 進程]收到備庫發送的 r 報文後,檢查共享內存中的等待隊列 SHMQueue, 根據備庫反饋的位點結合 SHMQueue,喚醒那些等待隊列中睡眠的 主庫 backend 進程(WalSndWaitForWal)。
  • [主庫 backend 進程]被喚醒,當前事務成功提交,SQL 執行完成返回給客戶端。

最後總結

本文簡要分析了 PostgreSQL 同步流複製實現的關鍵點。整個流程比較複雜,涉及到資料庫多個角色進程間的相互協作,並且使用了多種數據結構和多種進程間 IPC 通信方法。對我們了解 PostgreSQL 底層實現很有幫助。 希望能幫到想了解這部分的實現細節的朋友,謝謝。

更多技術乾貨敬請關注云棲社區知乎機構號:阿里云云棲社區 - 知乎

推薦閱讀:

PostgreSQL 在國內公司應用的多嗎?
PostgreSQL入門到精通——世界上功能最強大的開源資料庫
阿里雲RDS金融資料庫(三節點版)系列文章之背景、理論篇
PostgreSQL 資料庫的前世今生
Vert.x裡面那些b格很高的玩意

TAG:PostgreSQL | 日誌 | 資料庫 |