和任何包含珍貴資料的東西一樣,PostgreSQL 資料庫也應該經常備份。儘管這個過程相當簡單, 但是我們還是應該理解做這件事所用的一些技巧和假設。
備份 PostgreSQL 資料有三種完全不同的方法:
SQL 轉儲
檔案系統層級備份
線上備份
每種備份都有自己的優點和缺點。
SQL 轉儲
SQL 轉儲的方法採用的主意是建立一個文字檔,這個文本裡面都是 SQL 命令,當把這個檔案回饋給伺服器時,將重建與轉儲時狀態一樣的資料庫。 PostgreSQL 為這個用途提供了應用工具 pg_dump。這條命令的基本用法是:
pg_dump dbname > outfile
正如你所見,pg_dump 把結果輸出到標準輸出。 我們下面就可以看到這樣做有什麼好處。
pg_dump 是一個普通的 PostgreSQL 用戶端應用(儘管是個相當聰明的東西。)這就意味著你可以從任何可以訪問該資料庫的遠端主機上面進行備份工作。 但是請記住 pg_dump 不會以任何特殊許可權運行。具體說來, 就是它必須要有你想備份的表的讀許可權,因此,實際上你幾乎總是要成為資料庫超級使用者。
要聲明 pg_dump 應該以哪個使用者身份進行聯結,使用命令列選項 -h host 和 -p port。 預設主機是本地主機或你的環境變數PGHOST聲明的值。 類似,預設連接埠是環境變數PGPORT或(如果它不存在的話)編譯好了的預設值。 (伺服器通常有相同的預設,所以還算方便。)
和任何其他 PostgreSQL 用戶端應用一樣, pg_dump 預設時用與當前作業系統使用者名稱同名的資料庫使用者名稱進行聯結。 要覆蓋這個名字,要麼聲明 -U 選項, 要麼設定環境變數PGUSER。 請注意 pg_dump 的聯結也和普通客戶應用一樣要通過客戶認證機制。
由 pg_dump 建立的備份在內部是一致的, 也就是說,在pg_dump啟動並執行時候對資料庫的更新將不會被轉儲。 pg_dump 工作的時候並不阻塞其他的對資料庫的操作。 (但是會阻塞那些需要排它鎖的操作,比如 VACUUM FULL。)
Important: 如果你的資料庫結構依賴於 OID (比如說用做外鍵),那麼你必須告訴 pg_dump 把 OID 也倒出來。 要倒 OID,可以使用 -o 命令列選項。 預設時也不會轉儲"大對象"。如果你使用大對象,請參考 pg_dump 的命令手冊頁。
從轉儲中恢複
pg_dump 產生的文字檔可以由 psql 程式讀取。 從轉儲中恢複的常用命令是
psql dbname < infile
這裡的 infile 就是你給pg_dump命令的 outfile參數。這條命令不會建立資料庫 dbname,你必須在執行psql 前自己從template0建立(也就是說,用命令 createdb -T template0 dbname)。 psql 支援類似 pg_dump 的選項用以控制資料庫伺服器位置和使用者名稱。 參閱 psql 的手冊擷取更多資訊。
在開始運行恢複之前,目標庫和所有在轉儲出來的庫中擁有對象的使用者, 以及曾經在某些對象上被賦予許可權的使用者都必須已經存在。 如果這些不存在,那麼恢複將失敗,因為恢複過程無法把這些對象恢複成原有的所有權和/或許可權。 (有時候你希望恢複許可權,不過通常你不需要這麼做。)
一旦完成恢複,在每個資料庫上運行 ANALYZE 是明智的舉動, 這樣最佳化器就有有用的統計資料了。你總是可以運行 vacuumdb -a -z 來 VACUUM ANALYZE 所有資料庫;這個等效於手工運行 VACUUM ANALYZE。
pg_dump 和 psql 可以通過管道讀寫, 這樣我們就可能從一台主機上將資料庫目錄轉儲到另一台主機上,比如
pg_dump -h host1 dbname | psql -h host2 dbname
Important: pg_dump產生的轉儲輸出是相對於template0的。這就意味著任何 加入到template1的語言,過程等都會經由 pg_dump 轉儲。結果是,在恢複的時候,如果你使用的是客戶化的template1, 那麼你必須從template0中建立空的資料庫,就象我們上面的例子那樣。
有關如何有效地向 PostgreSQL 裡裝載大量資料的建議.
使用 pg_dumpall
上面的方法在備份整個資料庫叢集的時候比較麻煩而且不方便。因此我們提供了 pg_dumpall 程式。 pg_dumpall 備份一個給出的叢集中的每個資料庫,同時還確保保留象使用者和組這樣的全域資料狀態。 這個命令的基本用法是:
pg_dumpall > outfile
產生的轉儲可以用 psql 恢複:
psql template1 < infile
(實際上,你可以聲明任意現有的資料庫進行串連,但是如果你是向一個空的資料庫裝載,那麼 template1 是你唯一的選擇。) 恢複pg_dumpall的轉儲的時候通常需要資料庫超級使用者權限,因為我們需要它來恢複使用者和組資訊。
處理大資料庫
因為 PostgreSQL 允許表的大小大於你的系統允許的最大檔案大小, 可能把錶轉儲到一個檔案會有問題,因為產生的檔案很可能比你的系統允許的最大檔案大。 因為 pg_dump 輸出到標準輸出,你可以用標準的 Unix 工具繞開這個問題:
使用壓縮的轉儲. 使用你熟悉的壓縮程式,比如說 gzip。
pg_dump dbname | gzip > filename.gz
用下面命令恢複:
createdb dbname
gunzip -c filename.gz | psql dbname
或者
cat filename.gz | gunzip | psql dbname
使用 split。. split 命令允許你 你用下面的方法把輸出分解成作業系統可以接受的大小。 比如,讓每個塊大小為 1 MB:
pg_dump dbname | split -b 1m - filename
用下面命令恢複:
createdb dbname
cat filename* | psql dbname
使用客戶化轉儲格式. 如果PostgreSQL是在一個安裝了zlib 壓縮庫的系統上製作的, 那麼客戶化轉儲格式將在寫入輸出檔案的時候壓縮資料。 它會產生和使用 gzip 類似大小的轉儲檔案, 但是還附加了一個優點:你可以有選擇地恢複庫中的表。 下面的命令用客戶化轉儲格式轉儲一個資料庫:
pg_dump -Fc dbname > filename
客戶化格式的轉儲不是指令碼,不能用於 psql, 而是需要使用 pg_restore 轉儲。 請參考 pg_dump 和 pg_restore 的手冊擷取細節。
注意
出 於向下相容的考慮,預設的時候 pg_dump 並不轉儲大對象。 要轉儲大對象,你必須使用客戶化或者 tar 輸出格式, 並且在 pg_dump 中使用-b選項。 參閱 pg_dump 手冊擷取詳細資料。 在 PostgreSQL 源碼樹的 contrib/pg_dumplo 路徑裡也包含一個可以轉儲大對象的程式。
另一個備份的策略是直接拷貝PostgreSQL用於存放資料庫資料的檔案。
tar -cf backup.tar /usr/local/pgsql/data
不過,你要受到兩個限制,令這個方法不那麼實用,或者至少比 pg_dump 的方法遜色一些:
為了進行有效備份,資料庫伺服器必須被關閉。 象拒絕所有聯結這樣的折衷的方法是不行的,因為總是有一些緩衝區資料存在。 (主要因為 tar 和類似的工具在做備份的時候並不對檔案系統的狀態做原子快照)。
如果你曾經深入瞭解了資料庫在檔案系統布局的細節,你可能試圖從對應的檔案或目錄裡備份幾個表或者資料庫。 這樣做是沒用的,因為包含在這些檔案裡的資訊只是部分資訊。還有一半資訊在提交記錄檔 pg_clog/*裡面,它包含所有事務的提交狀態。 只有擁有這些資訊,表檔案的資訊才是可用的。當然,試圖只恢複表和相關的 pg_clog 資料也是徒勞的,因為這樣會把資料庫叢集裡的所有其他沒有用的表的資訊都拿出來。 所以檔案系統的備份只適用於一個資料庫叢集的完整恢複。
另外一個檔案系統備份的方法是給資料目錄做一個"一致的快照", 條件是檔案系統支援這個功能(並且你願意相信它是實現正確的)。 典型的過程是製作一個包含資料庫的卷的"凍結快照", 然後把整個資料庫目錄(不僅僅是部分,見上文)從快照拷貝到備份裝置, 然後釋放凍結快照。這樣甚至在資料庫伺服器在啟動並執行時候都可以運轉。 不過,這樣建立的備份會把資料庫檔案儲存在一個沒有恰當關閉資料庫伺服器的狀態下; 因此,如果你在這個備份目錄下啟動資料庫伺服器, 它就會認為資料庫伺服器經曆過崩潰並且重放 WAL 日誌。這不是個問題,只要意識到它即可(並且確信在自己的備份中包含 WAL 檔案)。
如果你的資料庫分布在多個卷上(比如,資料檔案和 WAL 日誌在不同的磁碟上),那麼可能就沒有任何方法擷取所有卷上準確的同步凍結快照。 在你新聞這樣的情況下的一致性快照的技術之前,仔細閱讀你的檔案系統文檔。 最安全的方法是關閉資料庫伺服器足夠長的時間,以建立所有凍結快照。
還要說明的是,檔案系統備份不會比SQL轉儲小。恰恰相反,大多數情況下它要大。 (比如pg_dump 不用倒出索引,只是建立它們的命令。)
在 任何時候,PostgreSQL 都在叢集的資料目錄的 pg_xlog/ 子目錄裡維護著一套預寫記錄檔(WAL)。 這些日誌記錄著每一次對資料庫的資料檔案的修改的細節。這些日誌存在是為了防止崩潰:如果系統崩潰, 資料庫可以通過"重放"上次檢查點以來的日誌記錄以恢複資料庫的完整性。 但是,日誌的存在讓它還可以用於第三種備份資料庫的策略:我們可以組合檔案系統備份與 WAL 檔案的備份。 如果需要恢複,我們就恢複備份,然後重放備份了的WAL檔案,把備份恢複到當前的時間。 這個方法對管理員來說,明顯比以前的方法更複雜,但是有非常明顯的優勢:
在開始的時候我們不需要一個非常完美的一致的備份。任何備份內部的不一致都會被日誌重放動作修改正確 (這個和崩潰恢複時發生的事情沒什麼區別)。因此我們不需要檔案系統快照的功能, 只需要 tar 或者類似的歸檔工具。
因為我們可以把無限長的 WAL 檔案序列串連起來,所以連續的備份簡化為連續地對 WAL 檔案歸檔來實現。 這個功能對大資料庫特別有用,因為大資料庫的全備份可能並不方便。
我們可沒說重放 WAL 記錄的時候我們必須重放到結尾。我們可以在任意點停止重放, 這樣就有一個在任意時間的資料庫一致的快照。因此,這個支援人員即時恢複: 我們可以把資料庫恢複到你開始備份以來的任意時刻的狀態。
如果我們持續把 WAL 檔案序列填充給其它裝載了同樣的基礎備份檔案的機器, 我們就有了一套"熱備份"系統:在任何點我們都可以啟動第二台機器, 而它擁有近乎當前的資料庫拷貝。
和簡單的檔案系統備份技術一樣,這個方法只能支援整個資料庫叢集的恢複,而不是一個子集。 同樣,它還要求大量的Archive Storage:基礎備份量可能很大,而且忙碌的系統將產生許多兆需要備份的的 WAL 流量。 但是,它仍然時在需要高可靠性的場合下的最好的備份技術。
要想從線上備份中成功恢複,你需要一套連續的 WAL 歸檔檔案,它們最遠回朔到你開始備份的時刻。 因此,要想開始備份,你應該在開始第一次基礎備份之前設定並測試你的步驟。 根據我們討論過的歸檔 WAL 檔案的機制。
1. 設定 WAL 歸檔
抽象來看,一個運行著的 PostgreSQL 系統產生一個無限長的 WAL 日誌序列。 系統物理上把這個序列分隔成 WAL段檔案,通常一塊時 16M 位元組大 (在製作 PostgreSQL 的時候可以改變其大小)。 這些段檔案的名字是數值命名的,這些數值反映他們在抽取出來的 WAL 序列中的位置。 在不適用 WAL 歸檔的時候,系統通常只是建立幾個段檔案然後"迴圈"使用它們, 方法是把不再使用的段檔案的名字重新命名為更高的段編號。 系統假設那些內容比前一次檢查點更老的段檔案是沒用的了,然後就可以迴圈利用。
在歸檔 WAL 資料的時候,我們希望在每個段檔案填充滿之後捕獲之, 並且把這些資料在段檔案被迴圈利用之前儲存在某處。根據應用以及可用的硬體的不同, 我們可以有許多不同的方法"把資料儲存在某處": 我們可以把段檔案拷貝到一個 NFS 裝配的目錄,把它們放到另外一台機器上, 或者把它們寫入磁帶機裡(需要保證你有辦法把檔案恢複為原名), 或者把它們打成包,燒錄到 CD 裡,或者是其它的什麼方法。 為了給資料庫管理員提供最大可能性的靈活性,PostgreSQL 試圖不對如何歸檔做任何假設。取而代之的是,PostgreSQL 讓管理員聲明一個 shell 命令執行來拷貝一個完整的段檔案到它需要去的地方。 該命令可以簡單得就是一個 cp,或者它可以調用一個複雜的 shell 指令碼 — 所有都由管理員決定。
所使用的 shell 命令由配置參數 archive_command 聲明, 它實際上總是放在 postgresql.conf 檔案裡的。 在這個字串裡,任何 %p 都被要歸檔的檔案的絕對路徑代替,而任何 %f 只是被檔案名稱代替。 如果你需要在命令裡嵌入一個真正的 %,寫 %%。 最簡單的有用命令是類似下面這樣的
archive_command = 'cp -i %p /mnt/server/archivedir/%f </dev/null'
它將把 WAL 段拷貝到目錄 /mnt/server/archivedir。 這個只是一個例子,並非我們建議的方法,可能不能在所有系統上都正確運行。
歸檔命令將在運行 PostgreSQL 伺服器的同一個使用者的許可權下執行。 因此被歸檔的 WAL 檔案實際上包含你的資料庫裡的所有東西,所以你應該確保自己的歸檔資料不會被別人窺探; 比如,歸檔到一個沒有組或者全域讀許可權的目錄裡。
有一點很重要:若且唯若歸檔命令成功時,它才返回零。在得到一個零值結果之後, PostgreSQL 將假設該 WAL 段檔案已經成功歸檔, 因此它稍後將被刪除或者被新的資料覆蓋。但是,一個非零值告訴 PostgreSQL 該檔案沒有被歸檔; 因此它會周期性的重試直到成功。
歸檔命令通常應該設計成拒絕覆蓋已經存在的歸檔檔案。這是一個非常重要的安全特性, 可以在管理員操作失誤(比如把兩個不同的伺服器的輸出發送到同一個歸檔目錄)的時候保持你的歸檔的完整性。 我們建議你首先要測試你準備使用到歸檔命令,以保證它實際上不會覆蓋現有的檔案, 並且在這種情況下它返回非零狀態。 我們發現,在這方面, cp -i 在某些平台上是正確的,而在其它平台上是不正確的。 如果選定的命令本身並不能正確處理這個問題,你應該增加一個命令預先探測歸檔檔案是否存在。 比如,類似下面的東西。
archive_command = 'test ! -f .../%f && cp %p .../%f'
在幾乎所有的 Unix 變種上都工作正確。
在設計你的歸檔環境都時候,請考慮一下如果歸檔命令不停失敗會發生什麼情況, 因為有些方面要求操作者的幹涉,或者是歸檔空間不夠了。 比如,如果你往磁帶機上寫,但是沒有自動換片器,那麼就有可能發生這種情況; 如果磁帶滿了,那就除非換磁帶,否則啥事也做不了。 你應該確保人和錯誤條件或者人和要求操作員幹涉帶錯誤都會正確報告, 這樣才能迅速解決這些問題。否則 pg_xlog/ 目錄會不停地填充 WAL 段檔案, 直到問題解決。
歸檔命令的速度並不要緊,只要它能跟上你的伺服器產生 WAL 資料的平均速度即可。 即使歸檔進程落在了後面一點,正常的操作也會繼續進行。 如果歸檔進程慢很多,就會增加災難發生的時候丟失的資料量。 同時也意味著 pg_xlog/ 目錄包含大量未歸檔的日誌段檔案, 並且可能最後超出了磁碟空間。我們建議你監控歸檔進程,確保它是按照你的意識運轉的。
如果你關心能夠恢複到當前即時的狀態,你可能需要採取幾個額外的步驟以確保當前的, 部分填充的 WAL 段也拷貝到了某些地方。這條對於產生很少 WAL 流量的伺服器 (或者在運行中有鬆弛階段的)特別重要,因為在一個 WAL 段檔案完全填充滿進而可以歸檔之前, 可能需要很長時間。一個處理這些的可能的方法是設定一個 cron 作業, 周期性(比如每分鐘一次)地標識當前 WAL 段檔案然後把它們儲存到某個安全的地方。 歸檔的 WAL 段和儲存的當前段就足夠保證你可以總是恢複到目前時間的一分鐘之內。 這個行為目前還不是內建於 PostgreSQL 的,因為我們不想把 archive_command 的定義複雜化,因為那樣就要要求它跟蹤成功歸檔但是卻又有不同時刻含義的同一個 WAL 檔案。 archive_command 只是用於處理那些不再改變的 WAL 段檔案; 除了錯誤重試之外,對於任何給出的檔案名稱他都只被調用一次。
在寫自己的歸檔命令的時候,你應該假設被歸檔的檔案最多 64 個字元長並且可以包含 ASCII 字母,數字,以及點的任意組合。 我們不必要記住原始的全路徑(%p),但是有必要記住檔案名稱(%f)。
請注意儘管 WAL 歸檔允許你回複任何對你的 PostgreSQL 資料庫的資料做的修改, 在最初的基礎備份之後,它還是不會回複對設定檔的修改(也就是說,postgresql.conf,pg_hba.conf 和 pg_ident.conf),因為這些檔案都是手工編輯的,而不是通過 SQL 操作來編輯的。 所以你可能會需要把你的設定檔放在一個日常檔案系統備份過程即可處理到的地方。
2. 進行一次基礎備份
進行基礎備份的過程相當簡單:
確保 WAL 歸檔開啟並且可以運轉。
以資料庫超級使用者身份串連到資料庫,發出命令
SELECT pg_start_backup('label');
這裡的 label 是任意你想使用的這次備份操作的唯一標識。 (一個好習慣是使用你想把備份轉儲檔案放置的目的地的全路徑。) pg_start_backup 用你的備份的資訊,在你的叢集目錄裡,建立一個備份標籤檔案, 叫做 backup_label。
至於你串連到叢集中的那個資料庫沒什麼關係。你可以忽略函數返回的結果; 但是如果它報告錯誤,那麼在繼續之前處理它。
執行備份,使用任何方便的檔案系統工具,比如 tar 或者 cpio。 這些操作過程中既不需要關閉資料庫,也不希望關閉資料庫的操作。
再次以資料庫超級使用者身份串連資料庫,然後發出命令
SELECT pg_stop_backup();
如果這個返回成功,你的工作就完成了。
我們不需要太關心在 pg_start_backup 和開始實際的備份之間開銷的時間, 也不需要太關心備份結束和 pg_stop_backup 之間的時間; 幾分鐘的延遲不會搞砸事情。不過,你必須確保這些操作是按順序執行的而不是重疊執行的。
要保證你的備份轉儲包括所有資料庫叢集目錄裡的檔案(比如,/usr/local/pgsql/data)。 如果你在使用並未放置在這個目錄裡的資料表空間,也要小心地包含它們 (並且要確保你的備份轉儲歸檔符號串連是符號串連,否則,恢複會把你的資料表空間搞亂)。
不過,你可以在備份轉儲檔案裡省略叢集目錄裡的 pg_xlog/ 子目錄。 這個略微複雜些的動作是值得的,因為它減少了恢複的時候的錯誤。 如果 pg_xlog/ 是一個指向叢集目錄之外的一個符號串連,那麼這件事情很容易處理, 出於效能考慮的時候經常這麼做。
要使用這個備份,你需要儲存所有備份開始以及之後的 WAL 段檔案。 為了協助你實現這個任務,pg_stop_backup 函數建立一個備份曆史檔案, 它馬上儲存到 WAL 歸檔地區。這個檔案的名字是以你在使用備份的時候需要的第一個 WAL 段檔案的名字命名的。 比如,如果開始 WAL 檔案是 0000000100001234000055CD,那麼備份曆史檔案將命名為類似 0000000100001234000055CD.007C9330.backup 這樣的東西。 (這個檔案名稱的第二部分表示在該 WAL 檔案裡面的準確位置,通常可以被忽略。) 一旦你安全地把備份轉儲檔案歸了檔,那麼你就可以刪除所有那些數值名字在這個檔案前面的歸檔的 WAL 段。 備份曆史檔案只是一個小的文字檔。它包含你給予 pg_start_backup 的標籤字串, 以及備份的起始時間和終止時間。如果你使用這個標籤來表示轉儲檔案放在哪裡, 如果需要的話,那麼歸檔的曆史檔案就足夠告訴你轉儲檔案存放在哪裡了。
因為你必須保留直到你最後一次基礎備份的所有歸檔的 WAL 檔案, 那麼兩次基礎備份之間的間隔通常是根據你想在歸檔 WAL 檔案上花多少儲存空間來定的。 你還應該考慮你準備在恢複上花多少時間,如果需要恢複的話 — 系統將需要重放所有那些段, 而如果最後一次基礎備份以來,時間已經很長了,那麼那些動作可能會花掉好些時間。
還有一件事值得一提,那就是 pg_start_backup 函數在資料庫叢集目錄裡建立了一個叫 backup_label 的檔案,它被 pg_stop_backup 刪除。 這個檔案當然也會作為你的備份轉儲檔案的一部分歸檔。這個備份標籤檔案包含你給予 pg_start_backup 的標籤字串, 以及 pg_start_backup 啟動並執行時刻,以及起始 WAL 檔案的名字。 如果有混淆,那麼我們可以看看備份轉儲檔案裡面然後判斷轉儲檔案來自那個備份會話。
我們還可以在 postmaster 停止的時候製作一個備份轉儲。 在這種條件下,很明顯你不能使用 pg_start_backup 或者 pg_stop_backup, 並且因此你必須靠自己的手段來跟蹤備份轉儲檔案都是那些,以及相關的 WAL 檔案最遠走到哪裡。 通常使用上面的線上備份步驟更好些。
3. 從線上備份中恢複
好,最糟糕的事情發生了,現在你需要從備份中恢複。下面是步驟:
停止 postmaster,如果它還在啟動並執行話。
如果你還有足夠的空間,把整個叢集資料目錄和所有資料表空間拷貝到一個臨時位置, 以防萬一你之後還需要它們。請注意這個預防措施要求你在系統裡又足夠的剩餘空間來現有庫的保持兩份拷貝。 如果你沒有足夠的空間,那麼你至少需要把叢集資料目錄的 pg_xlog 子目錄的內容拷貝到安全的地方, 因為它們可能包含系統宕掉的時候還沒有歸檔的日誌。
然後清理掉所有在該叢集資料目錄裡的現存檔案, 以及所有你使用的資料表空間雷根目錄下的現存檔案。
從你的備份轉儲中恢複資料庫檔案。要小心用正確的所有者(資料庫系統使用者,而不是 root!)和許可權恢複它們。 如果你使用了資料表空間,你可能需要核實在 pg_tblspc/ 裡的符號串連都得到正確恢複。
刪除任何目前還在 pg_xlog/ 裡的檔案;這些檔案來自備份轉儲,因此它們可能比目前的老。 如果你就根本沒有歸檔 pg_xlog/,那麼重建之,要注意也要重建子目錄 pg_xlog/archive_status/。
如果你有在步驟 2 裡面儲存的 WAL 段檔案,那麼把它們拷貝到 pg_xlog/。 (最好是拷貝它們,而不是把它們移動回來,這樣即使發生了糟糕的事情,你需要重啟的時候, 你也依然擁有未修改的檔案。)
在叢集資料目錄裡建立一個恢複命令檔案 recovery.conf(參閱 Recovery Settings)。 你可能還需要臨時修改 pg_hba.conf 以避免普通使用者串連,直到你確信恢複已經正常了為止。
啟動 postmaster。postmaster 將進入復原模式並且繼續讀取它需要的歸檔的 WAL 檔案。 在恢複過程完成後,postmaster 將把 recovery.conf 改名為 recovery.done (以避免不小心因後面的崩潰再次進入復原模式)然後開始正常的資料庫操作。
檢查資料庫的內容以確保你已經恢複到你期望的位置。 如果還沒有,回到步驟 1。如果全部正常,則恢複 pg_hba.conf 成正常狀態,允許你的使用者登入。
所有這些操作的關鍵區段時設定一個恢複命令檔案, 這個檔案描述你希望如何恢複以及恢複應該走到哪裡。 你可以使用 recovery.conf.sample(通常安裝在安裝目錄的 share/ 子目錄裡)作為原型。 你必須在 recovery.conf 裡面聲明的一個東西是 restore_command, 它告訴系統如何拿迴歸檔的 WAL 檔案段。類似 archive_command, 這個是一個指令碼命令字串。它可以包含 %f,這個變數會被需要的記錄檔名替換, 以及 %p,它會被要拷貝去的記錄檔的絕對路徑代替。 如果需要在命令裡替換真正的 %,寫 %%。 最簡單的有用命令是類似下面的東西
restore_command = 'cp /mnt/server/archivedir/%f %p'
這個命令將把以前歸檔的 WAL 段從目錄 /mnt/server/archivedir 拷貝過來。 你當然可以使用某些更複雜的東西,甚至是一個要求操作者裝配合適的磁帶的 shell 指令碼。
重要的一點是:該命令在失敗的時候返回非零值。如果記錄檔沒有出現在規檔中,那麼該系統將詢問該命令; 在問到的時候,它必須返回非零。這個不是錯誤條件。還要注意 %p 路徑的基礎名將和 %f 不一樣; 不要認為它們是可以互換的。
在歸檔中找不到的 WAL 段將被認為在 pg_xlog/ 裡;這樣就允許使用最近沒有歸檔的段。 但是在歸檔中的段將比 pg_xlog/ 中的優先。在檢索歸檔的檔案的時候,系統將不會覆蓋現有的 pg_xlog/ 內容。
通常,恢複將處理所有可用的 WAL 段,因此把資料庫恢複到目前時間(或者是在所給出的可用 WAL 段數目的情況下, 我們能走到的最近的地方)。但是如果你想恢複到某些以前的時刻點(比如,就在菜鳥 DBA 刪除你的主要事務表之前), 那麼只需要在 recovery.conf 裡聲明要求的停止點。你可以通過日期/時間來聲明, 也可以通過特定事務 ID 的結束來聲明這個停止點,我們叫做"恢複目標"。 在我們寫到這些的時候,只有日期/時間選項比較有用, 因為我們沒有工具來協助你精確地標識應該使用哪個事務 ID。
注意: 請注意停止點必須在備份的終止時間之後(也就是,pg_stop_backup 的時間)。 你無法使用一個基礎備份恢複到備份進行中中的某個時刻。 (要想恢複到該時刻,你必須回到你以前的基礎備份,然後從那個位置向前滾動。)
3.1. 恢複設定
這些設定只能在 recovery.conf 裡面使用,並且只是在恢複的過程中起作用。 在任何之後的恢複中,你必須重新設定他們。恢複過程開始後,它們的值無法改變。
restore_command (string)
執行檢索歸檔 WAL 檔案段序列的 shell 命令。這個參數是必須的。 字串中的任何 %f 都被從歸檔中檢索出來的檔案名稱替換, 而任何 %p 都被替換為拷貝過去的伺服器上的絕對路徑。 需要在命令裡嵌入真正的 % 字元時,寫 %%。
有一點很重要,那就是這個命令只有在成功的時候才返回零。 系統會向這條命令詢問沒有在歸檔裡出現的檔案名稱; 在這種情況下,它必須返回非零。比如:
restore_command = 'cp /mnt/server/archivedir/%f "%p"'
restore_command = 'copy /mnt/server/archivedir/%f "%p"' # Windows
recovery_target_time (timestamp)
這個參數聲明恢複執行到達的時間戳記。最多可以聲明一個 recovery_target_time 或 recovery_target_xid。預設是恢複到 WAL 日誌的結尾。 精確的停止點也受 recovery_target_inclusive 影響。
recovery_target_xid (string)
這 個參數聲明恢複將到達的事務 ID。要注意的是,儘管事務 ID 在事務開始的時候是認為順序的, 但是事務可以以不同的數值順序完成。將要恢複的事務是那些在聲明的這個事務之前(可以選擇包括它提交的時候的)提交的。 最多可以聲明一個 recovery_target_xid 或 recovery_target_time。 預設是恢複到 WAL 日誌的結尾。精確的停止點也受 recovery_target_inclusive 影響。
recovery_target_inclusive (boolean)
聲 明我們是否在恢複目標之後(true),還是正好在其之前(false)停止。 適用於 recovery_target_time 和 recovery_target_xid, 不管聲明的是哪個。它分別表示具有準確的提交時間或者 ID 的那些(個)事務,是否將包含在恢複之中。 預設是 true。
recovery_target_timeline (string)
聲明恢複到一個特定的時間軸。預設是恢複到進行基礎備份時的當時的目前時間線上。 只是在複雜的重新恢複的情況下,你才需要設定這個參數,也就是在你需要恢複到一個本身是在即時恢複之後到達的狀態下, 才需要這麼做。 參閱 Section 22.3.4 進行討論。
4. 時間軸
能夠把資料庫恢複到以前的某個時間點的能力導致了一些類似科幻小說裡的時間跟蹤和並行宇宙這樣的複雜情況。 在資料庫的最初的歷史裡,可能你在周二下午 5:15 刪除掉了一個非常關鍵的表。 然後有條不紊地拿出備份,恢複到周二晚上 5:14 即時備份。在這個資料庫宇宙的歷史裡, 你從來沒有刪除過那個表。但是假如你後來認識到這麼幹並非絕好的主意,並且想回到最初的曆史中的稍後的點。 你沒法這麼幹,因為在資料庫啟動並執行時候,它覆蓋了一些 WAL 段檔案的序列,這些序列就是在你希望回去的區間裡的。 因此你的確需要區分在你從那些未經處理資料庫曆史產生的 WAL 中完成即時恢複之後產生的 WAL 序列。
為了處理這些問題,PostgreSQL 有個叫時間軸的概念。 每次你即時恢複到一個比 WAL 序列的結尾要早的時刻,那麼就建立一個新的時間軸, 以表示在該次恢複之後產生的 WAL 記錄。(不過,如果恢複動作一尺處理到 WAL 的結尾, 我們就不會開始一個新的時間軸:我們只是擴充現有個那個。)時間軸 識別碼是 WAL 段檔案名稱的一部分, 因此新的時間軸並不會覆蓋以前的時間軸產生的 WAL 資料。實際上我們可以歸檔許多不同的時間軸。 雖然這些看起來像沒用的特性,但它卻可能常常是救命稻草。考慮一下你並不很確信應該恢複到那個時刻的情況, 這個時候你不得不做好幾次實驗性即時恢複然後從中找到舊歷史中最好的分支。 如果沒有時間軸,那麼這個過程可能很快就會導致無法管理的混亂。 有了時間軸,你可以恢複到任意以前的狀態, 包括恢複到你後來放棄的時間軸分支的狀態。
每當建立一個新的時間軸的時候,PostgreSQL 都建立一個"時間軸曆史"檔案, 它顯示自己從那個時間軸分出來,以及何時分出來的。這些曆史檔案是在從包含多個時間軸的規黨中進行恢複時, 允許系統選取正確的 WAL 段檔案的必要檔案。因此,它們像 WAL 段檔案一樣歸檔到 WAL 歸檔裡。 曆史檔案只是很小的文字檔(不想段檔案很大),所以獨立地儲存他們代價很小,也值得做。 如果你喜歡,你可以在曆史檔案裡加入注釋,錄自己為什麼設定一個時間軸以及如何設定的等資訊。 這樣的注釋會在你有厚厚一堆不同的時間軸需要選擇和分析的時候特別有價值。
恢複的預設的行為時沿著與備份基礎備份的同一個時間軸恢複。 如果你像恢複到某些子時間軸(也就是,你想回到某些本身就是在開始恢複企圖之後發生的狀態), 你需要在 recovery.conf 裡聲明目標時間軸 ID。你無法恢複到比基礎備份更早的時間軸分支。
5. 注意
在我們寫到這些的時候,線上備份技術還有幾個局限。它們可能在將來的版本中修補:
在非 B-tree 索引上的操作(散列,R-tree,和 GiST 索引)目前沒有用 WAL 記錄日誌, 所以重放就不會更新這些索引類型。我們建議的繞開方法是在完成恢複操作之後手工 REINDEX 每個這樣的索引。
還要注意,目前的 WAL 格式佔地非常大,因為它包含許多磁碟影像。 這麼做對於崩潰恢複用途是合適的,因為我們可能需要修補部分寫入的磁碟頁。 但是對 PITR 操作卻沒必要儲存如此多頁面。將來開發的一個方面就是通過刪除無用的頁拷貝來壓縮歸檔的 WAL 資料。