PgSQL · 內核解析 · 同步流複製實現分析
摘要: PostgreSQL 的流複製自引入以來以穩定著稱,近幾年的幾個大版本陸續完成了好幾個大特性,例如 遠程物理備份 同步流複製 級聯流複製 邏輯流複製 讓流複製在整個 PostgreSQL 技術方案中扮演越來越重要的角色。
原文
PostgreSQL 的流複製自引入以來以穩定著稱,近幾年的幾個大版本陸續完成了好幾個大特性,例如
- 遠程物理備份
- 同步流複製
- 級聯流複製
- 邏輯流複製
讓流複製在整個 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 | 日誌 | 資料庫 |