閱讀343 返回首頁    go 技術社區[雲棲]


PostgreSQL服務器管理:備份和恢複

本文檔為PostgreSQL 9.6.0文檔,本轉載已得到原譯者彭煜瑋授權。

SQL 轉儲方法的思想是創建一個由SQL命令組成的文件,當把這個文件回饋給服務器時,服務器將利用其中的SQL命令重建與轉儲時狀態一樣的數據庫。 PostgreSQL為此提供了工具pg_dump。這個工具的基本用法是:


pg_dump dbname > outfile

正如你所見,pg_dump把結果輸出到標準輸出。我們後麵將看到這樣做有什麼用處。 盡管上述命令會創建一個文本文件,pg_dump可以用其他格式創建文件以支持並行 和細粒度的對象恢複控製。

pg_dump是一個普通的PostgreSQL客戶端應用(盡管是個 相當聰明的東西)。這就意味著你可以在任何可以訪問該數據庫的遠端主機上進行備份工作。但是請記住 pg_dump不會以任何特殊權限運行。具體說來,就是它必須要有你想備份的表的讀 權限,因此為了備份整個數據庫你幾乎總是必須以一個數據庫超級用戶來運行它(如果你沒有足夠的特權 來備份整個數據庫,你仍然可以使用諸如-n schema 或-t table選項來備份該數據庫中你能夠 訪問的部分)。

要聲明pg_dump連接哪個數據庫服務器,使用命令行選項-hhost和 -p port。 默認主機是本地主機或你的PGHOST環境變量指定的主機。 類似地,默認端口是環境變量PGPORT或(如果PGPORT不存在)內建的默認值。 (服務器通常有相同的默認值,所以還算方便。)

和任何其他PostgreSQL客戶端應用一樣, pg_dump默認使用與當前操作係統用戶名同名的數據庫用戶名進行連接。 要使用其他名字,要麼聲明-U選項,要麼設置環境變量PGUSER。請注意pg_dump的連接也要通過客戶認證機製(在Chapter 20裏描述)。

pg_dump對於其他備份方法的一個重要優勢是,pg_dump的輸出可以很容易地在新版本的PostgreSQL中載入,而文件級備份和連續歸檔都是極度的服務器版本限定的。pg_dump也是唯一可以將一個數據庫傳送到一個不同機器架構上的方法,例如從一個32位服務器到一個64位服務器。

由pg_dump創建的備份在內部是一致的, 也就是說,轉儲表現了pg_dump開始運行時刻的數據庫快照,且在pg_dump運行過程中發生的更新將不會被轉儲。pg_dump工作的時候並不阻塞其他的對數據庫的操作。 (但是會阻塞那些需要排它鎖的操作,比如大部分形式的ALTER TABLE)

1.1. 從轉儲中恢複

pg_dump生成的文本文件可以由psql程序讀取。 從轉儲中恢複的常用命令是:


psql dbname < infile

其中infile就是pg_dump命令的輸出文件。這條命令不會創建數據庫dbname,你必須在執行psql前自己從template0創建(例如,用命令createdb -T template0 dbname)。psql支持類似pg_dump的選項用以指定要連接的數據庫服務器和要使用的用戶名。 參閱psql的手冊獲取更多信息。 非文本文件轉儲可以使用pg_restore工具來恢複。

在開始恢複之前,轉儲庫中對象的擁有者以及在其上被授予了權限的用戶必須已經存在。如果它們不存在,那麼恢複過程將無法將對象創建成具有原來的所屬關係以及權限(有時候這就是你所需要的,但通常不是)。

默認情況下,psql腳本在遇到一個SQL錯誤後會繼續執行。你也許希望在遇到一個SQL錯誤後讓psql退出,那麼可以設置ON_ERROR_STOP變量來運行psql,這將使psql在遇到SQL錯誤後退出並返回狀態3:


psql --set ON_ERROR_STOP=on dbname < infile

不管怎樣,你將隻能得到一個部分恢複的數據庫。作為另一種選擇,你可以指定讓整個恢複作為一個單獨的事務運行,這樣恢複要麼完全完成要麼完全回滾。這種模式可以通過向psql傳遞-1或--single-transaction命令行選項來指定。在使用這種模式時,注意即使是很小的一個錯誤也會導致運行了數小時的恢複被回滾。但是,這仍然比在一個部分恢複後手工清理複雜的數據庫要更好。

pg_dump和psql讀寫管道的能力使得直接從一個服務器轉儲一個數據庫到另一個服務器成為可能,例如:


pg_dump -h host1 dbname | psql -h host2 dbname

Important: pg_dump產生的轉儲是相對於template0。這意味著在template1中加入的任何語言、過程等都會被pg_dump轉儲。結果是,如果在恢複時使用的是一個自定義的template1,你必須從template0創建一個空的數據庫,正如上麵的例子所示。

一旦完成恢複,在每個數據庫上運行ANALYZE是明智的舉動,這樣優化器就有有用的統計數據了。

1.2. 使用pg_dumpall

pg_dump每次隻轉儲一個數據庫,而且它不會轉儲關於角色或表空間(因為它們是集簇範圍的)的信息。為了支持方便地轉儲一個數據庫集簇的全部內容,提供了pg_dumpall程序。pg_dumpall備份一個給定集簇中的每一個數據庫,並且也保留了集簇範圍的數據,如角色和表空間定義。該命令的基本用法是:


pg_dumpall > outfile

轉儲的結果可以使用psql恢複:


psql -f infile postgres

(實際上,你可以指定恢複到任何已有數據庫名,但是如果你正在將轉儲載入到一個空集簇中則通常要用(postgres)。在恢複一個pg_dumpall轉儲時常常需要具有數據庫超級用戶訪問權限,因為它需要恢複角色和表空間信息。如果你在使用表空間,請確保轉儲中的表空間路徑適合於新的安裝。

pg_dumpall工作時會發出命令重新創建角色、表空間和空數據庫,接著為每一個數據庫pg_dump。這意味著每個數據庫自身是一致的,但是不同數據庫的快照並不同步。

集簇範圍的數據可以使用pg_dumpall的--globals-only選項來單獨轉儲。如果在單個數據庫上運行pg_dump命令,上述做法對於完全備份整個集簇是必需的。

1.3. 處理大型數據庫

在一些具有最大文件尺寸限製的操作係統上創建大型的pg_dump輸出文件可能會出現問題。幸運地是,pg_dump可以寫出到標準輸出,因此你可以使用標準Unix工具來處理這種潛在的問題。有幾種可能的方法:

使用壓縮轉儲。. 你可以使用你喜歡的壓縮程序,例如gzip:


pg_dump dbname | gzip > filename.gz

恢複:


gunzip -c filename.gz | psql dbname

或者:


cat filename.gz | gunzip | psql dbname

使用split。. split命令允許你將輸出分割成較小的文件以便能夠適應底層文件係統的尺寸要求。例如,讓每一塊的大小為1兆字節:


pg_dump dbname | split -b 1m - filename

恢複:


cat filename* | psql dbname

使用pg_dump的自定義轉儲格式。. 如果PostgreSQL所在的係統上安裝了zlib壓縮庫,自定義轉儲格式將在寫出數據到輸出文件時對其壓縮。這將產生和使用gzip時差不多大小的轉儲文件,但是這種方式的一個優勢是其中的表可以被有選擇地恢複。下麵的命令使用自定義轉儲格式來轉儲一個數據庫:


pg_dump -Fc dbname > filename

自定義格式的轉儲不是psql的腳本,隻能通過pg_restore恢複,例如:


pg_restore -d dbname filename

對於非常大型的數據庫,你可能需要將split配合其他兩種方法之一進行使用。

使用pg_dump的並行轉儲特性。. 為了加快轉儲一個大型數據庫的速度,你可以使用pg_dump的並行模式。它將同時轉儲多個表。你可以使用-j參數控製並行度。並行轉儲隻支持“目錄”歸檔格式。


pg_dump -j num -F d -f out.dir dbname

你可以使用pg_restore -j來以並行方式恢複一個轉儲。它隻能適合於“自定義”歸檔或者“目錄”歸檔,但不管歸檔是否由pg_dump -j創建。

另外一種備份策略是直接複製PostgreSQL用於存儲數據庫中數據的文件,Section 18.2解釋了這些文件的位置。你可以采用任何你喜歡的方式進行文件係統備份,例如:


tar -cf backup.tar /usr/local/pgsql/data

但是這種方法有兩個限製,使得這種方法不實用,或者說至少比pg_dump方法差:

1.為了得到一個可用的備份,數據庫服務器必須被關閉。例如阻止所有連接的半路措施是不起作用的(部分原因是tar和類似工具無法得到文件係統狀態的一個原子的快照,還有服務器內部緩衝的原因)。關於停止服務器的信息可以在Section 18.5中找到。不用說,在恢複數據之前你也需要關閉服務器。

2.如果你已經深入地了解了數據庫的文件係統布局的細節,你可能會有興趣嚐試通過相應的文件或目錄來備份或恢複特定的表或數據庫。這種方法也不會起作用,因為包含在這些文件中的信息隻有配合提交日誌文件(pg_clog/*)才有用,提交日誌文件包含了所有事務的提交狀態。一個表文件隻有和這些信息一起才有用。當然也不可能隻恢複一個表及相關的pg_clog數據,因為這會導致數據庫集簇中所有其他表變得無用。因此文件係統備份值適合於完整地備份或恢複整個數據庫集簇。

另一種文件係統備份方法是創建一個數據目錄的"一致快照",如果文件係統支持此功能(並且你相信它的實現正確)。典型的過程是創建一個包含數據庫的卷的"凍結快照",然後從該快照複製整個數據目錄(如上,不能是部分複製)到備份設備,最後釋放凍結快照。即使在數據庫服務器運行時,這種方式也有效。但是,以這種方式創建的備份保存的文件看起來就像數據庫沒有被正確關閉時的狀態。因此,當你從備份數據上啟動數據庫服務器時,它會認為上一次的服務器實例崩潰了並嚐試重放WAL日誌。這不是問題,隻是需要注意(當然WAL文件必須要包括在備份中)。你可以在拍攝快照之前執行一次CHECKPOINT以便節省恢複時間。

如果你的數據庫跨越多個文件係統,可能沒有任何方式可以對所有卷獲得完全同步的凍結快照。例如,如果你的數據文件和WAL日誌放置在不同的磁盤上,或者表空間在不同的文件係統中,可能沒有辦法使用快照備份,因為快照必須是同步的。在這些情況下,一定要仔細閱讀你的文件係統文檔以了解其對一致快照技術的支持。

如果沒有可能獲得同步快照,一種選擇是將數據庫服務器關閉足夠長的時間以建立所有的凍結快照。另一種選擇是執行一次連續歸檔基礎備份(Section 25.3.2),因為這種備份對於備份期間發生的文件係統改變是免疫的。這要求在備份過程中允許連續歸檔,恢複時使用連續歸檔恢複。

還有一種選擇是使用rsync來執行一次文件係統備份。其做法是先在數據庫服務器運行時執行rsync,然後關閉數據庫服務器足夠長時間來做一次rsync --checksum (--checksum是必需的,因為rsync的文件修改 時間粒度隻能精確到秒)。第二次rsync會比第一次快,因為它隻需要傳送相對很少的數據,由於服務器是停止的,所以最終結果將是一致的。這種方法允許在最小停機時間內執行一次文件係統備份。

注意一個文件係統備份通常會比一個SQL轉儲體積更大(例如pg_dump不需要轉儲索引的內容,而是轉儲用於重建索引的命令)。但是,做一次文件係統備份可能更快。

在任何時間,PostgreSQL在數據集簇目錄的pg_xlog/子目錄下都保持有一個預寫式日誌(WAL)。這個日誌存在的目的是為了保證崩潰後的安全:如果係統崩潰,可以"重放"從最後一次檢查點以來的日誌項來恢複數據庫的一致性。該日誌的存在也使得第三種備份數據庫的策略變得可能:我們可以把一個文件係統級別的備份和WAL文件的備份結合起來。當需要恢複時,我們先恢複文件係統備份,然後從備份的WAL文件中重放來把係統帶到一個當前狀態。這種方法比之前的方法管理起來要更複雜,但是有其顯著的優點:

  • 我們不需要一個完美的一致的文件係統備份作為開始點。備份中的任何內部不一致性將通過日誌重放(這和崩潰恢複期間發生的並無顯著不同)來修正。因此我們不需要文件係統快照功能,隻需要tar或一個類似的歸檔工具。
  • 由於我們可以結合一個無窮長的WAL文件序列用於重放,可以通過簡單地歸檔WAL文件來達到連續備份。這對於大型數據庫特別有用,因為在其中不方便頻繁地進行完全備份。
  • 並不需要一直重放WAL項一直到最後。我們可以在任何點停止重放,並得到一個數據庫在當時的一致快照。這樣,該技術支持時間點恢複:在得到你的基礎備份以後,可以將數據庫恢複到它在其後任何時間的狀態。
  • 如果我們連續地將一係列WAL文件輸送給另一台已經載入了相同基礎備份文件的機器,我們就得到了一個熱備份係統:在任何時間點我們都能提出第二台機器,它差不多是數據庫的當前副本。

Note:
pg_dump和pg_dumpall不會產生文件係統級別的備份,並且不能用於連續歸檔方案。這類轉儲是邏輯的並且不包含足夠的信息用於WAL重放。

就簡單的文件係統備份技術來說,這種方法隻能支持整個數據庫集簇的恢複,卻無法支持其中一個子集的恢複。另外,它需要大量的歸檔存儲:一個基礎備份的體積可能很龐大,並且一個繁忙的係統將會產生大量需要被歸檔的WAL流量。盡管如此,在很多需要高可靠性的情況下,它是首選的備份技術。

要使用連續歸檔(也被很多數據庫廠商稱為"在線備份")成功地恢複,你需要一個從基礎備份時間開始的連續的歸檔WAL文件序列。為了開始,在你建立第一個基礎備份之前,你應該建立並測試用於歸檔WAL文件的過程。對應地,我們首先討論歸檔WAL文件的機製。

3.1. 建立WAL歸檔

抽象地來說,一個運行中的PostgreSQL係統產生一個無窮長的WAL記錄序列。係統從物理上將這個序列劃分成WAL 段文件,通常是每個16MB(段尺寸在構建PostgreSQL時可修改)。段文件會被分配一個數字名稱以便反映它在整個抽象WAL序列中的位置。在沒有使用WAL歸檔時,係統通常隻創建少量段文件,並且通過重命名不再使用的段文件為更高的段編號來"回收"它們。係統假設內容位於最後一個檢查點之前的段文件是無用的且可以被回收。

在歸檔WAL數據時,我們需要在每一段被填充滿時捕捉其內容,並且在段文件被回收重用之前保存該數據。依靠應用和可用的硬件,有很多不同的方法來"保存數據":我們可以將段文件拷貝到一個已掛載的位於另一台機器上的NFS目錄,或者將它們寫出到一個磁帶驅動器(確保你有辦法標識每個文件的原始文件名),或者將它們批量燒錄到CD上,或者其他什麼方法。為了向數據庫管理員提供靈活性,PostgreSQL不對如何歸檔做任何假設。取而代之的是,PostgreSQL讓管理員聲明一個shell命令來拷貝一個完整的段文件到它需要去的地方。 該命令可以簡單得就是一個cp,或者它可以調用一個複雜的 shell 腳本 — 所有都由你決定。

要啟用WAL歸檔,需設置wal_level配置參數為replica或更高,設置archive_mode為on,並且使用archive_command配置參數指定一個shell命令。實際上,這些設置總是被放置在postgresql.conf文件中。在archive_command中,%p會被將要歸檔的文件路徑所替代,而%f隻會被文件名所替代(路徑名是相對於當前工作目錄而言的,即集簇的數據目錄)。如果你需要在命令中嵌入一個真正的%字符,可以使用%%。最簡單的命令類似於:


archive_command = 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'  # Unix
archive_command = 'copy "%p" "C:\\server\\archivedir\\%f"'  # Windows

它將把 WAL 段拷貝到目錄/mnt/server/archivedir(這個隻是一個例子,並非我們建議的方法,可能不能在所有係統上都正確運行)。在%p和%f參數被替換之後,實際被執行的命令看起來可能是:


test ! -f /mnt/server/archivedir/00000001000000A900000065 && cp pg_xlog/00000001000000A900000065 /mnt/server/archivedir/00000001000000A900000065

對每一個將要被歸檔的新文件都會生成一個類似的命令。

歸檔命令將在運行PostgreSQL服務器的同一個用戶的權限下執行。因為被歸檔的一係列WAL 文件實際上包含你的數據庫裏的所有東西,所以你應該確保自己的歸檔數據不會被別人窺探; 比如,歸檔到一個沒有組或者全局讀權限的目錄裏。

有一點很重要:當且僅當歸檔命令成功時,它才返回零退出。在得到一個零值結果之後,PostgreSQL將假設該文件已經成功歸檔, 因此它稍後將被刪除或者被新的數據覆蓋。但是,一個非零值告訴PostgreSQL該文件沒有被歸檔; 因此它會周期性的重試直到成功。

歸檔命令通常應該被設計成拒絕覆蓋已經存在的歸檔文件。這是一個非常重要的安全特性, 可以在管理員操作失誤(比如把兩個不同的服務器的輸出發送到同一個歸檔目錄)的時候保持你的歸檔的完整性。

我們建議你首先要測試你準備使用到歸檔命令,以保證它實際上不會覆蓋現有的文件, 並且在這種情況下它返回非零狀態。以上Unix中的命令例子通過包含一個獨立的test步驟來保證這一點。在某些Unix平台上,cp具有諸如-i的開關,可用來更簡潔地完成這一切,但是在沒有驗證返回的退出狀態正確之前你不能依賴它們(特別地,GNU的cp在使用-i時將對已存在的目標文件返回狀態零,這並不是我們所期望的行為)。

在設計你的歸檔環境時,請考慮一下如果歸檔命令不停失敗會發生什麼情況, 因為有些情況要求操作者的幹涉,或者是歸檔空間不夠了。 例如,如果你往磁帶機上寫,但是沒有自動換帶機,那麼就有可能發生這種情況; 如果磁帶滿了,除非換磁帶,否則任何事也做不了。 你應該確保任何錯誤情況或者任何要求操作員幹涉的情況都會被正確報告, 這樣才能迅速解決這些問題。否則pg_xlog/目錄會不停地被WAL段文件填充, 直到問題解決(如果包含pg_xlog/的文件係統被填滿,PostgreSQL將會做一次致命關閉。不會有未提交事務被丟失,但是數據庫將會保持離線直到你釋放一部分空間)。

歸檔命令的速度並不要緊,隻要它能跟上你的服務器生成 WAL 數據的平均速度即可。即使歸檔進程稍微落後,正常的操作也會繼續進行。 如果歸檔進程慢很多,就會增加災難發生的時候丟失的數據量。這同時也意味著pg_xlog/目錄包含大量未歸檔的段文件, 並且可能最後超出了可用磁盤空間。我們建議你監控歸檔進程,確保它是按照你的期望運轉的。

在寫自己的歸檔命令的時候,你應該假設被歸檔的文件名最長為64個字符並且可以包含 ASCII 字母、數字以及點的任意組合。 我們不需要保持原始的相對路徑(%p),但是有必要保持文件名(%f)。

請注意盡管 WAL 歸檔允許你恢複任何對你的PostgreSQL數據庫中數據所做的修改, 但它不會恢複對配置文件的修改(即postgresql.conf、pg_hba.conf 和 pg_ident.conf),因為這些文件都是手工編輯的,而不是通過 SQL 操作來編輯的。 所以你可能會需要把你的配置文件放在一個日常文件係統備份過程可處理的位置。

歸檔命令隻會為完成的WAL段調用。因此如果你的服務器產生了一點點WAL流量(或者在產生時有寬鬆的周期),從一個事務完成到它被安全地記錄在歸檔存儲中之間將會有較長的延遲。要為未歸檔數據設置一個年齡限製,你可以設置archive_timeout來強製要求服務器按照其設定的頻度切換到一個新的WAL段。注意由於強製切換而被歸檔的文件還是具有和完全歸檔的文件相同的長度。因此設置一個很短的archive_timeout是很不明智的 — 它會膨脹你的歸檔存儲。將archive_timeout設置為1分鍾左右通常是合理的。

同樣,如果你希望確保一個剛剛完成的事務能被盡快歸檔,可以使用pg_switch_xlog進行一次手動段切換。其他與WAL管理相關的使用函數在Table 9-78中列出。

當wal_level為minimal時,一些SQL命令被優化為避免記錄WAL日誌。在這些語句的其中之一的執行過程中如果打開了歸檔或流複製,WAL中將不會包含足夠的信息用於歸檔恢(崩潰恢複不受影響)。出於這個原因,wal_level隻能在服務器啟動時修改。但是,archive_command可以通過重載配置文件來修改。如果你希望暫時停止歸檔,一種方式是將archive_command設置為空串('')。這將導致WAL文件積累在pg_xlog/中,直到一個可用的archive_command被重新建立。

3.2. 製作一個基礎備份

執行一次基礎備份最簡單的方法是使用pg_basebackup工具。它將會以普通文件或一個tar歸檔的方式創建一個基礎備份。如果需要比pg_basebackup更高的靈活性,你也可以使用低級API來製作一個基礎備份。

沒有必要關心創建一個基礎備份所需的時間。但是,如果你正常地運行停用了full_page_writes的服務器,你可能會注意到備份運行時的性能下降,因為full_page_writes在備份模式期間會被實際強製實施。

要使用備份,你將需要保留所有在文件係統備份期間及之後生成的WAL段文件。為了便於你做這些,基礎備份過程會創建一個備份曆史文件,它將被立刻存儲到WAL歸檔區域。該文件以文件係統備份中你需要的第一個WAL段文件命名。例如,如果開始的WAL文件是0000000100001234000055CD,則備份曆史文件將被命名為0000000100001234000055CD.007C9330.backup.(文件名的第二部分表明WAL文件中的一個準確位置,一般可以被忽略)。一旦你已經安全地歸檔了文件係統備份和在備份過程中被使用的WAL段文件(如備份曆史文件中所指定的) ,所有名字在數字上低於備份曆史文件中記錄值的已歸檔WAL段對於恢複文件係統備份就不再需要了,並且可以被刪除。但是你應該考慮保持多個備份集以絕對保證你能恢複你的數據。

備份曆史文件是一個很小的文本文件。它包含你指定給pg_basebackup的標簽字符串,以及備份的起止時間以及起止WAL段。如果你使用該標簽來標識相關轉儲文件,則已歸檔的曆史文件足以說明需要哪個轉儲文件進行恢複。

由於你不得不保存最後一次基礎備份之後的所有歸檔WAL文件,基礎備份之間的間隔通常應該根據你希望在歸檔WAL文件上花費的存儲空間來設定。你也應該考慮你準備花多長時間來進行恢複,如果需要恢複 — 係統將不得不重放所有那些WAL段,如果這些WAL段覆蓋了最後一次基礎備份以後的很長時間,重放過程將會花費一些時間。

3.3. 使用低級API製作一個基礎備份

使用低級API製作一個基礎備份的過程比pg_basebackup方法要包含更多的步驟,但相對要更簡單。很重要的一點是,這些步驟要按照順序執行,並且在執行下一步之前要驗證上一步是否成功。

可以用非排他或者排他的方法來製作低級基礎備份。我們推薦非排他方法,而排他 的方法已經被廢棄並且最終將被去除。

3.3.1. 製作一個非排他低級備份

非排他低級備份允許其他並發備份運行(既包括那些使用同樣的 備份 API 開始的備份,也包括那些使用 pg_basebackup開始的備份)。

1.確保WAL歸檔被啟用且正在工作。

2.作為一個具有運行 pg_start_backup 權利的用戶(超級用戶,或者被授予在該 函數上 EXECUTE 的用戶)連接到服務器(不在乎是哪個數據庫)並且發出命令:


SELECT pg_start_backup('label', false, false);

其中label是用來唯一標識這次備份操作的任意字符串。調用 pg_start_backup的連接必須被保持到備份結束,否則備份 將被自動中止。

默認情況下,pg_start_backup可能需要較長的時間完成。 這是因為它會執行一個檢查點,並且該檢查點所需要的 I/O 將會分散到一段 顯著的時間上,默認情況下是你的檢查點間隔(見配置參數 checkpoint_completion_target)的一半。這通常 是你所想要的,因為它可以最小化對查詢處理的影響。如果你想要盡可能快地 開始備份,請把第二個參數改成true。

第三個參數為false會告訴pg_start_backup 開始一次非排他基礎備份。

3.使用任何趁手的文件係統備份工具(例如tar或者 cpio,不是pg_dump 或者pg_dumpall)執行備份。當你做這些 時,不需要也不值得停止正常的數據庫操作。

4.在同一個連接中,發出命令:


SELECT * FROM pg_stop_backup(false);

這會終止備份模式並且執行一次自動切換到下一個 WAL 段。要做切換的 原因是讓在備份期間寫入的最後一個 WAL 段文件能準備好被歸檔。

pg_stop_backup將返回一個具有三個值的行。這些域的 第二個應該被寫入到該備份根目錄中名為backup_label的 文件。第三個域應該被寫入到一個名為tablespace_map 的文件,除非該域為空。這些文件對該備份正常工作來說是至關重要的, 不能被隨意修改。

5.一旦備份期間活動的 WAL 段文件被歸檔,備份就完成了。由 pg_stop_backup的第一個返回值標識的文件是構成一個 完整備份文件集合所需的最後一個段。如果archive_mode 被啟用,直到最後一個段被歸檔前pg_stop_backup都不會 返回。從你已經配置好archive_command之後這些文件的 歸檔就會自動發生。在大部分情況下,這些歸檔會很快發生,但是建議你監 控你的歸檔係統確保沒有延遲。如果歸檔進程由於歸檔命令的失敗而落後, 它將會持續重試知道歸檔成功並且備份完成。如果你希望對 pg_stop_backup的執行給出一個時間限製,可以設置一個 合適的statement_timeout值,但要注意如果 pg_stop_backup因此而中止會致使備份可能失效。

3.3.2. 製作一個排他低級備份

一個排他備份的處理絕大部分都和非排他備份相同,但是在一些關鍵步驟上 不同。它不允許超過一個並發備份運行,並且如果服務器在備份期間崩潰,會 有一些問題。在 PostgreSQL 9.6 之前,這是唯一可用的低級方法,但是現在 推薦所有用戶在可能的情況下升級他們的腳本來使用非排他備份。

1.確保 WAL 歸檔被啟用且正常工作。

2.作為一個具有運行 pg_start_backup 權利的用戶(超級用戶,或者被授予在該 函數上 EXECUTE 的用戶)連接到服務器(不在乎是哪個數據庫)並且發出命令:


SELECT pg_start_backup('label');

這裏label是任何你希望用來唯一標識這個備份操作的字符串。 pg_start_backup在集簇目錄中創建一個關於備份信息的 備份標簽文件,也被稱為backup_label, 其中包括了開始時間和標簽字符串。該函數也會在集簇目錄中創建一個 名為tablespace_map的表空間映射文件, 如果在pg_tblspc/中有一個或者多個表空間符號鏈接存在, 該文件會包含它們的信息。如果你需要從備份中恢複,這兩個文件對於備份的 完整性都至關重要。

默認情況下,pg_start_backup會花費很長時間來完成。這是因為它會執行一個檢查點,而檢查點所需要的I/O在相當一段時間內將會被傳播,默認情況下這段時間是內部檢查點間隔的一半(參見配置參數checkpoint_completion_target)。這通常是你所希望的,因為它能將對查詢處理的影響最小化。如果你要盡快開始備份,可使用:


SELECT pg_start_backup('label', true);

這會使檢查點盡可能快地被完成。

3.使用任何方便的文件係統備份工具執行備份,例如tar 或cpio(不是pg_dump 或pg_dumpall)。在此期間,不需要也 不值得停止正常的數據庫操作。在備份期間要考慮的事情可見 Section 25.3.3.3小節。

4.再次以具有運行 pg_stop_backup 權利的用戶(超級用戶,或者已經被授予 該函數上 EXECUTE 的用戶)連接到數據庫並且發出命令:


SELECT pg_stop_backup();

這將終止備份模式,並且執行一個自動切換到下一個WAL段。進行切換的原因是將在備份期間生成的最新WAL段文件安排為可歸檔。

5.一旦備份期間活動的WAL段文件被歸檔,你的工作就完成了。 pg_stop_backup的結果所標識的文件是構成一個完整備份 文件組所需的最新段。如果archive_mode被啟用,直到最 新段被歸檔pg_stop_backup都不會返回。由於你已經配置了 archive_command,這些文件的歸檔過程會自動發生。在 大部分情況下這會很快發生,但還是建議你監控你的歸檔係統來確保不會有 延遲。如果歸檔處理由於歸檔命令的錯誤而延遲,它會保持重試直到歸檔成 功和備份完成。如果你希望在pg_stop_backup的執行上 設置一個時間限製,可對statement_timeout設 置一個合適的值,但要注意如果pg_stop_backup因此而 中止會致使備份可能失效。

3.3.3. 備份數據目錄

如果被拷貝的文件在拷貝過程中發生變化,某些文件係統備份工具會發出警告或錯誤。在建立一個活動數據庫的基礎備份時,這種情況是正常的,並非一個錯誤。然而,你需要確保你能夠把它們和真正的錯誤區分開。例如,某些版本的rsync為"消失的源文件"返回一個獨立的退出碼,且你可以編寫一個驅動腳本來將該退出碼接受為一種非錯誤情況。同樣,如果一個文件在被tar複製的過程中被截斷,某些版本的GNU tar會返回一個與致命錯誤無法區分的錯誤代碼。幸運的是,如果一個文件在備份期間被改變,版本為1.16及其後的GNU tar將會退出並返回1,而對於其他錯誤返回2。在版本1.23及其後的GNU tar中,你可以使用警告選項--warning=no-file-changed --warning=no-file-removed來隱藏相關的警告消息。

確認你的備份包含數據庫集簇目錄(例如/usr/local/pgsql/data)下的所有文件。如果你使用了不在此目錄下的表空間,注意也把它們包括在內(並且確保你的備份將符號鏈接歸檔為鏈接,否則恢複過程將破壞你的表空間)。

不過,你應當從備份中忽略集簇的pg_xlog/子目錄中的文件。這種微小的調整是值得的,因為它降低了恢複時的錯誤風險。如果pg_xlog/是一個指向位於集簇目錄之外其他地方的符號鏈接就很容易安排了,這是一種出於性能原因的常見設置。你可能也希望排除postmaster.pid和postmaster.opts,它們記錄了關於postmaster運行的信息,但與最終使用這個備份的postmaster無關(這些文件可能會使pg_ctl搞混淆)。

從備份中忽略集簇的pg_replslot/子目錄中的文件通常也是個好主意,這樣 主控機上存在的複製槽不會成為備份的一部分。否則,後續用該備份創建一個後備機可能會導致該 後備機上的WAL文件被無限期保留,並且在啟用了熱後備反饋的情況下可能導致主控機膨脹,因為使用 那些複製槽的客戶端將繼續連接到主控機(而不是後備機)並且繼續更新其上的槽。即使該備份是要被 用來創建一個新的主控機,拷貝複製槽也不是特別有用,因為這些槽的內容在新主控機上線時很可能已 經過時。

備份標簽文件包含你指定給 pg_start_backup的標簽字符串,以及 pg_start_backup被運行的時刻和起始WAL文件的名字。 在發生混亂的情況下就可以在備份文件中查看並準確地決定該轉儲文件來 自於哪個備份會話。表空間映射文件包括存在於目錄pg_tblspc/ 中的符號鏈接名稱以及每一個符號鏈接的完整路徑。這些文件不僅是為了供參考, 它們的存在和內容對於係統恢複過程的正確操作是至關重要。

在服務器停止時也可以創建一個備份。在這種情況下,你顯然不能使用 pg_start_backup或pg_stop_backup, 並且因此你隻能依靠你的自己的策略來跟蹤哪個備份是哪個, 以及相關WAL文件應該走回到什麼程度。通常最好遵循上麵的連續歸檔過程。

3.4. 使用一個連續歸檔備份進行恢複

好,現在最壞的情況發生了,你需要從你的備份進行恢複。這裏是其過程:

1.如果服務器仍在運行,停止它。

2.如果你具有足夠的空間,將整個集簇數據目錄和表空間複製到一個臨時位置,稍後你將用到它們。注意這種預防措施將要求在你的係統上有足夠的空閑空間來保留現有數據庫的兩個拷貝。如果你沒有足夠的空間,你至少要保存集簇的pg_xlog子目錄的內容,因為它可能包含在係統垮掉之前還未被歸檔的日誌。

3.移除所有位於集簇數據目錄和正在使用的表空間根目錄下的文件和子目錄。

4.從你的文件係統備份中恢複數據庫文件。注意它們要使用正確的所有權恢複(數據庫係統用戶,不是root!)並且使用正確的權限。如果你在使用表空間,你應該驗證pg_tblspc/中的符號鏈接被正確地恢複。

5.移除pg_xlog/中的任何文件,這些是來自於文件係統備份而不是當前日誌,因此可以被忽略。如果你根本沒有歸檔pg_xlog/,那麼以正確的權限重建它。注意如果以前它是一個符號鏈接,請確保你也以同樣的方式重建它。

6.如果你有在第2步中保存的未歸檔WAL段文件,把它們拷貝到pg_xlog/(最好是拷貝而不是移動它們,這樣如果在開始恢複後出現問題你任然有未修改的文件)。

7.在集簇數據目錄中創建一個恢複命令文件recovery.conf(見Chapter 27)。你可能還想臨時修改pg_hba.conf來阻止普通用戶在成功恢複之前連接。

8.啟動服務器。服務器將會進入到恢複模式並且進而根據需要讀取歸檔WAL文件。恢複可能因為一個外部錯誤而被終止,可以簡單地重新啟動服務器,這樣它將繼續恢複。恢複過程結束後,服務器將把recovery.conf重命名為recovery.done(為了阻止以後意外地重新進入恢複模式),並且開始正常數據庫操作。

9.檢查數據庫的內容來確保你已經恢複到了期望的狀態。如果沒有,返回到第1步。如果一切正常,通過恢複pg_hba.conf為正常來允許用戶連接。

所有這些的關鍵部分是設置一個恢複配置文件,它描述你希望如何恢複以及恢複要運行到什麼程度。你可以使用recovery.conf.sample(通常在安裝的share/目錄中)作為一個原型。你絕對必須在recovery.conf中指定的是restore_command,它告訴PostgreSQL如何獲取歸檔WAL文件段。與archive_command相似,這也是一個shell命令字符串。它可以包含%f(將被期望的日誌文件名替換)和%p(將被日誌文件被拷貝的目標路徑名替換)。(路徑名是相對於當前工作目錄的,即集簇的數據目錄)。如果你需要在命令中嵌入一個真正的%字符,可以寫成%%。最簡單的命令類似於:


restore_command = 'cp /mnt/server/archivedir/%f %p'

它將從目錄/mnt/server/archivedir中拷貝之前歸檔的WAL段。當然,你可以使用更複雜的,甚至是一個要求操作者裝載合適磁帶的shell腳本。

重要的是命令在失敗時返回非零退出狀態。該命令將被調用來請求不在歸檔中的文件, 在這種情況下它應該返回非零值。這不是一種錯誤情況。一種例外是該命令被一個信號(除了被用作數 據庫服務器關閉動作一部分的SIGTERM)終止或者被shell的錯誤 (例如命令未找到)終止,那樣恢複將中止並且服務器將不會啟動。

並非所有被請求的文件都是WAL段文件,你也許還會請求一些具有.backup或 .history後綴的文件。還要注意的是,%p路徑的基本名字將會和%f 不同,但不要期望它們可以互換。

歸檔中找不到的WAL段可以在pg_xlog/中看到,這使得可以使用最近未歸檔的段。但是,在歸檔中可用的段將會被優先於pg_xlog/中的文件被使用。

通常,恢複將會處理完所有可用的WAL段,從而將數據庫恢複到當前時間點(或者盡可能接近給定的可 用WAL段)。因此,一個正常的恢複將會以一個"文件未找到"消息結束,錯誤消息的準確文 本取決於你選擇的restore_command。你也可能在恢複的開始看到一個針對名稱類 似於00000001.history文件的錯誤消息。這也是正常的並且不表示在簡單恢複情 況中的問題,對此的討論見Section 25.3.5。

如果你希望恢複到之前的某個時間點(例如,恢複到幼稚的DBA丟棄了你主要的交易表之前),隻需要 在recovery.conf中指定要求的停止點。你可以使用日期/時間、命名恢複點或一個 指定事務ID的結束時間來定義停止點(也被稱為"恢複目標")。在這種寫法中,隻有日期/時 間和命名恢複點選項非常有用,因為沒有工具可以幫助你準確地確定要用哪個事務ID。

Note:
停止點必須位於基礎備份的完成時間之後,即pg_stop_backup的完成時間。在備份過程中你不能使用基礎備份來恢複(要恢複到這個時間,你必須回到你之前的基礎備份並且從這裏開始前滾)。

如果恢複找到被破壞的WAL數據,恢複將會停止於該點並且服務器不會啟動。在這種情況下,恢複進程需要從開頭重新開始運行,並指定一個在損壞點之前的"恢複目標"以便恢複能夠正常完成。如果恢複由於一個外部原因失敗,例如一個係統崩潰或者WAL歸檔變為不可訪問,則該次恢複可以被簡單地重啟並且它將會從幾乎是上次失敗的地方繼續。恢複重啟工作起來很像普通操作時的檢查點:服務器周期性地強製把它的所有狀態寫到磁盤中,然後更新pg_control文件來說明已經處理過的WAL數據,這樣它們就不會被再次掃描。

3.5. 時間線

將數據庫恢複到一個之前的時間點的能力帶來了一些複雜性,這和有關時間旅行和平行宇宙的科幻小說有些相似。例如,在數據庫的最初曆史中,假設你在周二晚上5:15時丟棄了一個關鍵表,但是一直到周三中午才意識到你的錯誤。不用苦惱,你取出你的備份,恢複到周二晚上5:14的時間點,並上線運行。在數據庫宇宙的這個曆史中,你從沒有丟棄該表。但是假設你後來意識到這並非一個好主意,並且想回到最初曆史中周三早上的某個時間。你沒法這樣做,在你的數據庫在線運行期間,它重寫了某些WAL段文件,而這些文件本來可以將你引向你希望回到的時間。因此,為了避免出現這種狀況,你需要將完成時間點恢複後生成的WAL記錄序列與初始數據庫曆史中產生的WAL記錄序列區分開來。

要解決這個問題,PostgreSQL有一個時間線概念。無論何時當一次歸檔恢複完成,一個新的時間線被創建來標識恢複之後生成的WAL記錄序列。時間線ID號是WAL段文件名的一部分,因此一個新的時間線不會重寫由之前的時間線生成的WAL數據。實際上可以歸檔很多不同的時間線。雖然這可能看起來是一個無用的特性,但是它常常扮演救命稻草的角色。考慮到你不太確定需要恢複到哪個時間點的情況,你可能不得不做多次時間點恢複嚐試和錯誤,直到最終找到從舊曆史中分支出去的最佳位置。如果沒有時間線,該處理將會很快生成一堆不可管理的混亂。而有了時間線,你可以恢複到任何之前的狀態,包括早先被你放棄的時間線分支中的狀態。

每次當一個新的時間線被創建,PostgreSQL會創建一個"時間線曆史"文件,它顯示了新時間線是什麼時候從哪個時間線分支出來的。係統在從一個包含多個時間線的歸檔中恢複時,這些曆史文件對於允許係統選取正確的WAL段文件非常必要。因此,和WAL段文件相似,它們也要被歸檔到WAL歸檔區域。曆史文件是很小的文本文件,因此將它們無限期地保存起來的代價很小,而且也是很合適的(而段文件都很大)。如果你喜歡,你可以在一個曆史文件中增加注釋來記錄如何和為什麼要創建該時間線。當你由於試驗的結果擁有了一大堆錯綜複雜的不同時間線時,這種注釋將會特別有價值。

恢複的默認行為是沿著相同的時間線進行恢複,該時間線是基礎備份創建時的當前時間線。如果你希望恢複到某個子女時間線(即,你希望回到在一次恢複嚐試後產生的某個狀態),你需要在recovery.conf中指定目標時間線ID。你不能恢複到早於該基礎備份之前分支出去的時間線。

3.6. 建議和例子

這裏將給出一些配置連續歸檔的建議。

3.6.1. 單機熱備份

可以使用PostgreSQL的備份功能來產生單機熱備份。這些備份不能被用於時間點恢複,然而備份和恢複時要比使用pg_dump轉儲更快(它們也比pg_dump轉儲更大,所以在某些情況下速度優勢可能會被否定)。

在基礎備份的幫助下,產生一個單機熱備份最簡單的方式是使用pg_basebackup工具。如果你在調用它時使用了-X參數,使用該備份所需的所有事務日誌將會被自動包含在該備份中,並且恢複該備份也不需要特殊的動作。

如果在複製備份文件時需要更多靈活性,也可以使用一個較低層的處理來創建單機熱備份。要為低層 單機熱備份做準備,將wal_level設置為replica或更高, archive_mode設置為on,並且設置一個archive_command,該命令隻當一個開關文件存在時執行歸檔。例如:


archive_command = 'test ! -f /var/lib/pgsql/backup_in_progress || (test ! -f /var/lib/pgsql/archive/%f && cp %p /var/lib/pgsql/archive/%f)'

該命令在/var/lib/pgsql/backup_in_progress存在時執行歸檔,否則會安靜地返回0值退出狀態(讓PostgreSQL能回收不需要的WAL文件)。

通過這樣的準備,可以使用一個如下所示的腳本來建立備份:


touch /var/lib/pgsql/backup_in_progress
psql -c "select pg_start_backup('hot_backup');"
tar -cf /var/lib/pgsql/backup.tar /var/lib/pgsql/data/
psql -c "select pg_stop_backup();"
rm /var/lib/pgsql/backup_in_progress
tar -rf /var/lib/pgsql/backup.tar /var/lib/pgsql/archive/

開關文件/var/lib/pgsql/backup_in_progress首先被創建,這使對於未完成WAL文件的歸檔操作發生。備份完成之後開關文件會被刪除。歸檔的WAL文件則被加入到備份中,這樣基礎備份和所有需要的WAL文件都是同一個tar文件的組成部分。請記住在你的備份腳本中加入錯誤處理。

3.6.2. 壓縮的歸檔日誌

如果擔心歸檔存儲的尺寸,你可以使用gzip來壓縮歸檔文件:


archive_command = 'gzip < %p > /var/lib/pgsql/archive/%f'

那麼在恢複時你將需要使用gunzip:


restore_command = 'gunzip < /mnt/server/archivedir/%f > %p'

3.6.3. archive_command腳本

很多人選擇使用腳本來定義他們的archive_command,這樣他們的postgresql.conf項看起來非常簡單:


archive_command = 'local_backup_script.sh "%p" "%f"'

任何時候如果你希望在歸檔處理中使用多個命令,明智的方法是使用一個獨立的腳本文件。這可以使腳本更為複雜,它可以使用一種流行的腳本語言來編寫,例如bash 或perl。

需要在一個腳本內解決的需求例子包括:

  • 將數據拷貝到安全的場外數據存儲
  • 批處理WAL文件,這樣它們可以每三小時被傳輸一次,而不是一次一個
  • 與其他備份和恢複軟件交互
  • 與監控軟件交互以報告錯誤

Tip: 在使用一個archive_command腳本時,最好啟用logging_collector。任何從該腳本被寫到stderr的消息將出現在數據庫服務器日誌中,這允許在複雜配置失敗後能更容易被診斷。

3.7. 警告

在編寫此文檔時,連續歸檔技術存在一些限製。這可能會在未來的發布中被修複:

  • 哈希索引上的操作目前不被WAL記錄,因此重放不會更新這些索引。這將意味著任何新的插入都會被索引忽略,被更新的行顯然會消失而被刪除的行將仍然保留有指針。換句話說,如果你修改了一個具有哈希索引的表,那麼你將在一個後備服務器上得到不正確的查詢結果。當恢複結束時,推薦你手工REINDEX每一個這樣的索引。
  • 如果一個CREATE DATABASE命令在基礎備份時被執行,然後在基礎備份進行時CREATE DATABASE所複製的模板數據庫被修改,恢複中可能會導致這些修改也被傳播到已創建的數據庫中。這當然是我們不希望的。為了避免這種風險,最好不要在創建基礎備份時修改任何模板數據庫。
  • CREATE TABLESPACE命令會WAL以其字麵絕對路徑記錄,並且因此將在重放時以相同的絕對路徑來創建表空間。當日誌在一台不同的機器上被重放時,這可能也不是我們希望的。即使日誌在同一台機器上被重放也是危險的,就算是恢複到一個新的數據目錄重放過程也會覆蓋原來表空間的內容。為了避免這種潛在的陷阱,最佳做法是在創建或丟棄表空間後創建一個新的基礎備份。

還需要注意的是,默認的WAL格式相當龐大,因為它包括了很多磁盤頁快照。這些頁快照被設計用於支持崩潰恢複,因為我們可能需要修複斷裂的磁盤頁。依靠你的係統硬件和軟件,頁斷裂的風險可能會小到可以忽略,在此種情況下你可以通過使用full_page_writes參數關閉頁快照來顯著降低歸檔日誌的總容量(在這樣做之前閱讀Chapter 30中的注解和警告)。關閉頁快照並不會阻止使用日誌進行PITR操作。一個未來的開發點是通過移除不需要的頁拷貝來壓縮歸檔的WAL數據,即使full_page_writes為on。同時,管理員可能希望通過盡可能增大檢查點間隔參數來減少WAL中包含的頁快照數量。

最後更新:2017-08-23 15:02:23

  上一篇:go  ARMS最佳實踐- 零售業績實時數據展示
  下一篇:go  故紙堆:追溯平台