|
這段時間對 PostgreSQL 的備份恢復進行了一些研究, 有一些心得和大家分享一下.
我們知道, PostgreSQL 擁有 WAL(預寫式日志) 已經有一段時間了. WAL 的一個重要好處就是能在系統崩潰(數據庫崩潰甚至操作系統崩潰)的情況下, 仍然能夠保證數據的安全. 理想情況下就是恢復到系統崩潰前一刻的一致狀態.
WAL 是如何實現這一點的呢? 這里簡單探討一下.
PostgreSQL 數據目錄中包括一個子目錄叫 pg_xlog, 這里包含一些很 "整齊" 的文件 (文件名全都是16進制的數, 大小都是16MB(默認情況下)). 這些文件是聯機重做日志文件, 也就是很多 PostgreSQL 文檔中說的 XLog. 預寫式日志就表現在對 XLog 操作上. 當一個事務要提交, 已被修改的數據 必須先被寫到(嚴格來說應該是追加)到 XLog 中, 事務才能標識為 "已提交". 這樣, 就算主數據文件在崩潰中已經包含不完整的數據了, 仍然可以通過下面 的方法恢復到一致狀態:
1 找到前一個一致的數據庫狀態點(這稱為CheckPoint) 2 將 XLog 以快進的方式重新施加到主數據文件上, 直到崩潰前的時刻.
從這個角度來將, PostgreSQL 已經做得非常好了, 但仍然有一些問題. 比如, 如果介質(磁盤)發生故障, 整個數據庫文件, 包括主數據文件和日志 都不能讀取, 又該怎么辦呢?
目前, 我們只能定時通過 pg_dump 等工具把整個數據庫 dump 下來, 或者 關閉數據庫, 將數據目錄整個復制到另外的地方. 然后使用這樣的備份來恢復由介質故障引起的數據庫災難. 很顯然, 這不能滿足我們的要求, 通常, 我們都不可能以非常高 的頻率進行 pg_dump. 一旦發生災難, 就最多恢復到上一次執行 pg_dump 的時刻了.
沒有更好的辦法了嗎?
如果我們能對數據庫的做不間斷的增量備份, 不就可以到達我們的目的了嗎? 這個想法到是好, 可怎么捕捉對數據文件做的修改(同時還不要忘記事務的原子性)? 對數據文件的修改是分布于整個數據文件各處的, 很難對它們進行 所以, 這個方法是不現實的.
現實的方法還是要通過 WAL 系統來實現. 請注意前面我們已經討論過的, 對 XLog 的寫實際上是追加. 這一點使得要增量備份 XLog 成為可能. 目前, PostgreSQL 為了能限制 XLog 的大小, 采用了多個段(也就是多個文件)的方式, 循環利用磁盤空間 -- 當寫滿前一個 XLog 文件, 就產生一個新的, 并且讓 文件名代表 XLog 的編號, 同時, 如果可能, 刪除過期的 XLog 文件. 如果我們能在 PostgreSQL 刪除過期的 XLog 之前將它們復制到另外一個磁盤甚至其他 計算機, 不就能夠實現增量備份日志了嗎? 當災難發生的時候, 就可以在一個完整備份的基礎上, 連續施加備份的 XLog 進行 redu, 直至恢復到最后一次歸檔(復制到其他目錄或計算機)的日志.
實際上, 這種方法也正是 Oracle 數據庫的歸檔模式所采用的方法.
下面這個實驗可以加深理解
1 初始化數據庫目錄
$ initdb -D db
2 創建數據庫
$ pg_ctl -D db start $ createdb test $ psql test test=# create table t(a int); test=# insert into t values(1); test=# insert into t values(2); test=# insert into t values(3); test=# \q
3 全備份數據庫: 在不關閉數據庫(也就是說, 不要運行 pg_ctl -D db stop) 的情況下復制數據庫目錄. 注意, 這里采用了一種非常規手段, 僅僅是為了實驗, 不要在正式應用中使用. 目的是為了讓將來的恢復能自動開始.
$ cp -a db db.backup
4 繼續修改數據庫
$ psql test test=# insert into t values(100); test=# insert into t values(200); test=# insert into t values(300); test=# \q
5 備份日志(XLog)文件 (由于修改量很小, 實際上只有一個日志文件)
$ mkdir pg_xlog $ cp db/pg_xlog/* pg_xlog
6 模擬災難
$ pg_ctl -D db stop $ rm -rf db # 可以不用真的刪除, 只是認為它已經不存在了
7 進行災難恢復
$ cp -a db.backup db.restore $ cp -f pg_xlog/* db.restore/pg_xlog $ postmaster -D db.restore # 沒有用 pg_ctl 啟動, # 為了更清楚看到日志(此日志非彼日志)輸出 LOG: database system was interrupted at 2004-04-15 18:12:47 CST LOG: checkpoint record is at 0/9B1058 LOG: redo record is at 0/9B1058; undo record is at 0/0; shutdown TRUE LOG: next transaction ID: 536; next OID: 17142 LOG: database system was not properly shut down; automatic recovery in progress LOG: redo starts at 0/9B1098 LOG: record with zero length at 0/9D4458 LOG: redo done at 0/9D4434 LOG: database system is ready
$ psql test # 另外開一個控制臺 test=# select * from t; a ----- 1 2 3 100 200 300 (6 rows)
可見, 數據庫已經已經恢復了到了災難發生前的一刻.
當然, 這個實驗數據量很小只產生并復制了一個日志文件, 而且復制的日志文件 還是當前正在工作的, 和前面描述的不完全一致. 更深入的實驗大家可以下來做.
實際的聯機熱備份(也叫PITR(Point In Time Recovery))還有很多細節, 不過 總的來說, PostgreSQL 離實現聯機熱備份已經很近很近了! 也許下一個版本我們就能看到 這個令人興奮的功能了. 我們熱切地期待著!
她已經為我們做了這么多, 我們能為她做點什么呢?
以上內容僅代表我自己的理解, 不正之處敬請指出.
kernel 2004.4.15
轉自: http://bbs.pgsqldb.com/index.php?t=msg&th=3739&start=0
|