postgresql 預寫式日誌(Write Ahead Long)

在網站中看到一篇轉載次數非常高的文章,好多的日本網站也有。


文章的英文是postgresql的文章。該文章以postgresql 7.3版本爲基礎。相對於現在的新版本,其中沒有同步和異步的部分,同步與異步的設置在特定情況下以犧牲數據完整性風險爲前題,能大幅提高數據庫的處理性能。


儘管是篇中文的翻譯,但是對於瞭解postgresql的日誌管理有很大的幫助。原文名稱:PostgreSQL數據庫學習手冊之預寫式日誌


直接看內容:


 


Table of Contents


   11.1. 一般性描述


  11.1.1. 從 WAL 中獲取的直接好處


  11.1.2. 更多好處


  11.2. 實現


  11.3. WAL 配置


  作者: Vadim Mikheev 和 Oliver Elphick


  11.1. 一般性描述


  預寫式日誌 (WAL) 是一種實現事務日誌的標準方法.有關它的詳細描述可以在 大多數(如果不是全部的話)有關事務處理的書中找到.簡而言之,WAL 的中心思想是對數據文件 的修改(它們是表和索引的載體)必須是只能發生在這些修改已經 記錄了日誌之後 --也就是說,在日誌記錄沖刷到永久存儲器之後. 如果我們遵循這個過程,那麼我們就不需要在每次事務提交的時候都把數據頁沖刷到磁盤,因爲我們知道在出現崩潰的情況下, 我們可以用日誌來恢復數據庫:任何尚未附加到數據頁的記錄都將先從日誌記錄中重做(這叫向前滾動恢復,也叫做 REDO) 然後那些未提交的事務做的修改將被從數據頁中刪除 (這叫向後滾動恢復 -UNDO).


  11.1.1. 從 WAL 中獲取的直接好處


  使用 WAL 的第一個明顯的好處就是顯著地減少了磁盤寫的次數.因爲在日誌提交的時候只有日誌文件需要衝刷到磁盤; 在多用戶環境裏,許多事務的提交可以用日誌文件的一次 fsync()來完成.而且,日誌文件是順序寫的, 因此同步日誌的開銷要遠比同步數據頁的開銷要小.


  另外一個好處就是數據頁的完整性.實際情況是,在 WAL 之前,PostgreSQL 從來不能保證 在崩潰的情況下數據頁的完整性.在 WAL之前,在寫的過程中的任何崩潰都可能導致:


  1.  索引記錄指向一個不存在的表的行


  2.  索引記錄在分裂操作中丟失


  3.  完全崩潰了的表和索引頁的內容,因爲數據頁只寫了一部分


  索引的問題(問題 1 和 2)可能已經通過額外的 fsync() 調用修補好了,但是如果沒有 WAL,那麼沒有很明顯的 處理第三種情況的方法;WAL 在日誌裏保存整個 數據頁的內容 -- 如果那些內容在崩潰後的恢復中需要確保數據頁的完整性的話.


  11.1.2. 更多好處


  UNDO 操作還沒有實現。 這就意味着由退出的事務做的修改將仍然佔據磁盤空間, 因此我們仍然需要一個永久的 pg_clog 文件保存事務的狀態,因爲我們不能回收事務標識符.一旦實現了 UNDO, 那麼 pg_clog 就不再要求是永久的了; 我們就有可能在關閉的時候刪除pg_clog. (不過,這方面的緊迫性已經隨着我們對 pg_clog 採取分段存儲的方法而降低了 --- 我們不再需要永久保留pg_clog 記錄.)


  有了 UNDO,我們還可能實現 savepoints,這樣就允許非法事務操作的部分回捲(因爲誤敲了命令導致的分析器錯誤, 插入了重複的主鍵/唯一鍵字等等)同時還能夠繼續或提交該事務在發生錯誤之前的合法操作.目前,任何錯誤都將使整個事務成爲非法並且 要求事務退出.


  WAL 還提供了數據庫在線備份和恢復(backup and restore (BAR))的新方法. 要使用這個方法,我們可能要經常性地把數據文件保存到另外一個磁盤,磁帶或者另外一臺主機並且還要備份 WAL 日誌文件. 那麼數據庫文件拷貝和日誌歸檔文件就可以用於象災難恢復中那樣恢復數據.每次做完新數據庫文件以後,這個老的日誌文件就可以刪除了. 實現這個設施可能需要記錄數據文件和索引創建和刪除的日誌;同時還需要開發一種方法來拷貝數據文件(操作系統拷貝命令是不合適的).


  認識這些事情的好處的一個困難是它們要求在相當可觀的時間段內 保存 WAL 日誌(也就是說,如果需要事務的 UNDO, 那麼和可能的最長的事務的時間一樣長).目前的 WAL格式的體積相當大,因爲它包括多個磁盤頁的鏡像. 目前這還不是一個嚴重的問題,因爲這些日誌只需要保留一到兩個檢查點的時間間隔;但是爲了實現這些東西,以後我們可能需要某種壓縮的 WAL 格式.


  11.2. 實現


  在版本 7.1 以後,WAL 是自動打開的. 除了要求一些額外的磁盤空間存放 WAL 日誌以及一些必要的調節以外(參閱Section 11.3) ,對管理員沒有什麼其他要求,


  WAL 日誌存放在 $PGDATA/pg_xlog 目錄裏,它是作爲一個文件段的集合存儲的,每個段 16 MB 大. 每個段分割成 8 KB的頁.日誌記錄頭在 access/xlog.h 裏描述;日誌內容取決於 它記錄的事件的類型.段文件的名字是自然數,從0000000000000000開始.目前這些數字不能循環使用, 不過要把所有可用的數字都用光也需要非常長的時間.


  WAL 的緩衝區和控制結構在共享內存裏, 並且由後端操縱;它們是用輕量的鎖保護的.對共享內存的需求 由緩衝區數量決定.缺省的 WAL 緩衝區大小是 8 個 8 KB 的緩衝區,也就是 64KB.


  日誌位於和主數據庫文件不同的另外一個磁盤上會比較好. 你可以通過把pg_xlog目錄移動到另外 一個位置( postmaster 當然得關閉),然後在 $PGDATA 裏原來的位置創建一個 指向新位置的符號鏈接來實現.


  WAL 的目的是確保在數據庫記錄被修改之前, 先寫了日誌,但是這個目的有可能被那些向內核謊報成功寫的磁盤驅動器破壞,這時候,它們實際上只是緩衝了數據而並未把數據 存儲到磁盤上.這種情況下的電源失效仍然可能導致不可恢復的數據崩潰;管理員應該確保保存 PostgreSQL 的 日誌文件的磁盤不會做這種虛假彙報.


  11.2.1. 用 WAL 進行數據庫恢復


  在完成一個檢查點並且日誌文件沖刷了之後,檢查點的位置保存在了文件 pg_control 裏.因此在需要做恢復的時候, 後端首先讀取pg_control 和檢查點記錄; 然後它通過從檢查點記錄裏標識的日誌位置開始向前掃描.因爲數據頁的所有內容都保存在檢查點之後的第一個頁面修改的日誌裏, 所以自檢查點以來的所有變化都將被恢復到一個一致的狀態.


  用 pg_control 獲取檢查點位置可以加快恢復 進程的速度,但是爲了處理 pg_control 可能 的損壞,我們實際上應該實現對現存的日誌段的反向讀取順序 -- 從最新到最老 -- 這樣才能找到最後的檢查點.這些還沒有實現.


  11.3. WAL 配置


  有幾個與 WAL 相關的參數會影響數據庫性能. 本節討論它們的使用.參閱 Section 3.4 獲取配置參數的細節.


檢查點(Checkpoints)是事務的順序的點, 我們保證在該點之前的所有日誌信息都更新到數據文件中去了.在檢查點時,所有髒數據頁都沖刷到磁盤並且向日志文件中寫入一條 特殊的檢查點記錄.結果是,在發生崩潰的時候,恢復器就知道應該從日誌中的哪條記錄(稱做 redo 記錄)開始做 REDO 操作, 因爲在該記錄前的對數據文件的任何修改都已經在磁盤上了.在完成檢查點處理之後,任何在 undo 記錄之前寫的日誌段都不再 需要,因此可以循環使用或者刪除.(到基於 WAL 的 BAR(備份和恢復)實現的時候,這些日誌在 循環利用或者刪除之前將先歸檔.)


  postmaster 每隔一段時間就排生一個特殊的進程以創建下一個檢查點. 每隔 CHECKPOINT_SEGMENTS 個日誌段就創建一個檢查點, 或者每隔CHECKPOINT_TIMEOUT 秒創建一個. 以先到爲準.缺省設置分別是 3 個段和 300 秒. 我們也可以用 SQL 命令CHECKPOINT 強制一個檢查點.


  減少 CHECKPOINT_SEGMENTS 和/或CHECKPOINT_TIMEOUT 會令檢查點更頻繁一些. 這樣就允許更快的崩潰後恢復(因爲需要重做的工作更少).不過,我們必須在這個目地和更頻繁地衝刷髒數據頁所帶來的額外開銷之間 取得平衡.另外,爲了保證數據頁的一致性,在每個檢查點之後的第一次數據頁的變化會導致對整個頁面內容的日誌記錄.因此,檢查點時間間隔 短了會導致輸出到日誌中的數據的增加,會抵銷一部分縮短間隔的目標,並且怎麼着都會產生更多的磁盤 I/O.


  至少會有一個 16MB 的段文件,而且通常不會超過 2 *CHECKPOINT_SEGMENTS + 1 個文件。你可以用這些信息來估計 WAL 需要的空間。通常,如果一箇舊的日誌段文件不再需要了,那麼它將得到循環使用(重命名爲順序的下一個可用段)。 如果由於短期的日誌輸出峯值,導致了超過 2 *CHECKPOINT_SEGMENTS + 1 個段文件, 那麼到系統再次回到這個限制之內的時候,多於的段文件會被刪除,而不是循環使用。


  有兩個常用的 WAL 函數: LogInsert 和 LogFlush. LogInsert 用於向共享內存中的 WAL緩衝區里加一條新的記錄.如果沒有空間存放 新記錄,那麼LogInsert 就不得不寫出 (向內核緩存裏寫)一些填滿了的WAL緩衝.我們可不想這樣,因爲 LogInsert 用於 每次數據庫低層修改(比如,記錄插入),都要花在受影響的數據頁上持有一個排它鎖的時間,因爲該操作需要越快越好;更糟糕的是, 寫 WAL 緩衝可能還會強制創建新的日誌段, 它花的時間甚至更多.通常,WAL緩衝區應該由 一個 LogFlush 請求來寫和沖刷, 在大部分時候它都是發生在事務提交的時候以確保事務記錄被沖刷到永久存儲器上去了.在那些日誌輸入量比較大的系統上, LogFlush 請求可能不夠頻繁,這樣就不能避免 WAL 緩衝區被 LogInsert寫.在這樣的系統上,我們應該通過修改 postgresql.conf 的 WAL_BUFFERS 參數值來增加 WAL 緩衝區的數量.缺省的WAL 緩衝區數量是 8. 增加這個數值將有對應的共享內存使用量的增加.


  檢查點是開銷相當昂貴的操作,因爲它們用操作系統的sync() 調用強制所有髒的內核緩衝 刷新到硬盤上。繁忙的服務器可能會很快就把檢查點段文件填滿,導致額外的檢查點。如果這樣的強制檢查點發生的頻率比 CHECKPOINT_WARNING秒要頻繁,那麼將在服務器日誌裏輸出一條信息,建議你增加 CHECKPOINT_SEGMENTS。


  COMMIT_DELAY定義了後端在使用 LogInsert 向日志中寫了一條已提交 的記錄之後,在執行一次 LogFlush 之前休眠的毫秒數.這樣的延遲可以允許其它的後端把它們提交的記錄追加 到日誌中,這樣就可以用一次日誌同步把所有日誌沖刷到日誌中.如果沒有打開fsync或者當前少於 COMMIT_SIBLINGS 個其它後端處於活躍事務狀態的時候則不會發生休眠;這樣就避免了在其它事務一時半會不會提交的情況下睡眠. 請注意在大多數平臺上,休眠要求的分辯率是十毫秒, 所以任何介於 1 和 10000微秒之間的非零 COMMIT_DELAY 的作用都是一樣的. 適用這些參數的比較好的數值還不太清楚;我們鼓勵你多做試驗.


  WAL_SYNC_METHOD 參數決定PostgreSQL 如何 請求內核強制將 WAL 更新輸出到磁盤.只要滿足可靠性,那麼 所有選項應該都是一樣的,但是哪個最快則可能和平臺密切相關. 請注意如果你關閉了 FSYNC,那麼這個參數 就無所謂了.


  把 WAL_DEBUG 參數設置爲任何非零值都會導致 每次 LogInsert 和 LogFlush WAL 調用都被記錄到標準錯誤.目前,這個非零值是多少 沒有什麼區別.這個選項以後可能會被更通用的機制取代.


 


感謝中文提供者帶來的方便。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章