天天看點

PgSQL · 特性分析 · full page write 機制

pg預設每個page的大小為8k,pg資料頁寫入是以page為機關,但是在斷電等情況下,作業系統往往不能保證單個page原子地寫入磁盤,這樣就極有可能導緻部分資料塊隻寫到4k(作業系統是一般以4k為機關),這些“部分寫”的頁面包含新舊資料的混合。在崩潰後的恢複期間,xlog 裡面存儲的記錄變化資訊不夠完整,無法完全恢複該頁。pg為了解決這類問題,full_page_write機制孕育而生。

postgresql 在 checkpoint 之後在對資料頁面的第一次寫的時候會将整個資料頁面寫到 xlog 裡面。當出現主機斷電或者os崩潰時,redo操作時通過checksum發現“部分寫”的資料頁,并将xlog中儲存的這個完整資料頁覆寫目前損壞的資料頁,然後再繼續redo就可以恢複整個資料庫了。

除了能夠解決斷電等帶來壞資料頁問題外,full_page_write 還應用在線上備份功能上。pg進行全量備份資料庫一般通過pg_basebackup工具實作,pg_basebackup類似于copy操作,在此期間,也會出現部分資料頁寫到一半時檔案被copy走了,正是因為full_page_write存在,備份出來的資料庫才可以成功恢複啟動。是以即便full_page_write=off,在備份時也會被強制自動打開,保證備份成功。

full_page_write主要在xloginsert(插入一條xlog記錄)時發揮作用,通過full_page_writer開關狀态以及是否是checkpoint後對資料頁面的第一次修改(lsn<redorecptr)判斷是否需要備份資料頁。如果需要備份,那麼則把資料頁存放在這條記錄的末尾,最終寫入到xlog中。

在redo恢複的時候隻要資料塊有備份,那麼就是用備份的資料。

PgSQL · 特性分析 · full page write 機制

因為full_page_write需要在xlog中記錄資料頁,會寫更多xlog檔案,不僅有資料變化資訊,還有資料頁本身資訊,這樣會增加額外的io和磁盤消耗,同時也會引起主備延遲變大。

為了優化full_page_write,社群提供了一個patch,它的主要設計是建立兩個共享記憶體塊隊列,checkpoint專用buffer隊列和非checkpoint專用buffer隊列,同時關閉full_page_write。當使用者dml産生的資料buffer需要刷盤時,并不是立即刷到磁盤,而是先進入double write的buffer隊列,當buffer隊列滿時,則将buffer隊列裡面的資料首先刷到特别的double write檔案,然後再将資料刷到資料庫檔案。通過這種設計就不需要在checkpoint 之後在對資料頁面的第一次寫的時候會将整個資料頁面寫到 xlog 裡面。當資料庫需要恢複的時候,周遊所有double write檔案裡面的記錄塊,找到每個記錄塊對應的資料庫page,然後對這個page進行checksum,如果page損壞,那麼直接把記錄塊裡面的内容覆寫到buffer資料。最後把double write檔案删除,重新初始化buffer隊列。

PgSQL · 特性分析 · full page write 機制

把full_page_write這個選項關閉會提高資料庫執行速度以及減少xlog數量,但是可能導緻系統崩潰或者掉電之後的資料庫損壞。如果有減小部分頁面寫入風險的硬體支援(比如電池供電的磁盤控制器),或者檔案系統支援(能夠保證page寫入原子性),可以把風險降低到一個可以接受的範圍,那麼可以考慮關閉這個選項,其他情況下建議打開這個選擇。