1. 程式人生 > >Postgresql的三種備份方式

Postgresql的三種備份方式

        資料庫的備份有多種分類方式。按照備份後的檔案型別,可以分為物理備份(檔案系統級別的備份)和邏輯備份(備份後的檔案是sql檔案或特定格式的匯出檔案);按照備份過程中是否停止資料庫服務,可分為冷備份(備份過程中停止資料庫服務)和熱備份(備份過程中資料庫服務開啟並可供使用者訪問);按照備份是否是完整的資料庫,可分為全量備份(備份是完整的資料庫)和增量備份(備份是上一次全量備份後資料庫改變的內容)。

       Postgresql的常見備份方式有以下三種:

        1. 檔案系統級別的冷備份。

        這種備份方式需要關閉資料庫,然後拷貝資料檔案的完整目錄。恢復資料庫時,只需將資料目錄複製到原來的位置。該方式實際工作中很少使用。

 

        2. SQL轉儲。

        這裡我們用到的工具是pg_dump和pg_dumpall。

        這種方式可以在資料庫正在使用的時候進行完整一致的備份,並不阻塞其它使用者對資料庫的訪問。它會產生一個指令碼檔案,裡面包含備份開始時,已建立的各種資料庫物件的SQL語句和每個表中的資料。可以使用資料庫提供的工具pg_dumpall和pg_dump來進行備份。pg_dump只備份資料庫叢集中的某個資料庫的資料,它不會匯出角色和表空間相關的資訊,因為這些資訊是整個資料庫叢集共用的,不屬於某個單獨的資料庫。pg_dumpall,對集簇中的每個資料庫呼叫pg_dump

來完成該工作,還會還轉儲對所有資料庫公用的全域性物件(pg_dump不儲存這些物件)。 目前這包括適資料庫使用者和組、表空間以及適合所有資料庫的訪問許可權等屬性。

        例如,在我的計算機上,可使用如下命令對名為dbname的資料庫進行備份:

        pg_dump  –h 127.0.0.1  -p  5432  -U  postgres -c  –f  dbname.sql  dbname

        使用如下命令可對全部pg資料庫進行備份。

        pg_dumpall –h 127.0.0.1 –p 5432 -U postgres –c –f db_bak.sql

        恢復方式很簡單。執行恢復命令即可:

        psql –h 127.0.0.1 -p 5432 -U postgres –f db_bak.sql

 

       3. 連續歸檔

        這種方式的策略是把一個檔案系統級別的全量備份和WAL(預寫式日誌)級別的增量備份結合起來。當需要恢復時,我們先恢復檔案系統級別的備份,然後重放備份的WAL檔案,把系統恢復到之前的某個狀態。這種備份有顯著的優點:

  1. 不需要一個完美的一致的檔案系統備份作為開始點。備份中的任何內部不一致性將通過日誌重放來修正。
  2. 可以結合一個無窮長的WAL檔案序列用於重放,可以通過簡單地歸檔WAL檔案來達到連續備份。
  3. 不需要重放WAL項一直到最後。可以在任何點停止重放,並使資料庫恢復到當時的一致狀態。
  4. 可以連續地將一系列WAL檔案輸送給另一臺已經載入了相同基礎備份檔案的機器,得到一個實時的熱備份系統。

 

        如何進行連續歸檔呢?

        下面的例項中,作業系統為windows 10,Postgresql的版本為9.6。

        首先,需要修改postgresql.conf檔案的幾個引數修改如下:

        wal_level = ‘replica’

        archive_mode = ‘on’

        archive_command = 'copy  /y  "%p"  "D:\\archive\\%f"'

        archive_command執行時,%p會被要被歸檔的檔案路徑所替代,而%f只會被檔名所替代。如果你需要在命令中嵌入一個真正的%字元,可以使用%%。 “D:\\archive\\”替換為歸檔日誌的存放路徑,要確保歸檔的目錄是存在的。

        之後需要重啟資料庫使配置生效。

 

        接下來需要製作一個非排他的基礎備份。Postgresql提供了排他備份和非排他備份兩種備份方式,推薦使用非排他的備份方式。

        1. 作為一個具有執行 pg_start_backup 權利的使用者連線到伺服器(不在乎是哪個資料庫)並且發出命令:

Select pg_start_backup('backup_label', false, false);

        2. 對資料庫進行一次檔案系統級別的備份。即將postgresql的安裝目錄下的data目錄及其內容複製到其他位置。

        3. 在同一個連線中,發出命令:

        select * from pg_stop_backup(false);

         這個命令代表結束一次非排他的備份。

        現在來看基於時間點的恢復。

        假如你的資料庫出現了故障,需要恢復到之前的某個時刻的一致的狀態,就需要進行基於時間點的恢復。

其過程是:

1. 如果伺服器仍在執行,停止它。

2. 如果你具有足夠的空間,將整個集簇資料目錄和表空間複製到一個臨時位置。注意最好是拷貝而不是移動。如果你沒有足夠的空間,你至少要儲存集簇的pg_xlog子目錄的內容,因為它可能包含在系統垮掉之前還未被歸檔的日誌。

3. 移除data 目錄及其所有子檔案和子目錄。

4. 從檔案系統備份中恢復資料庫檔案。注意它們要使用正確的使用者恢復並且使用正確的許可權。如果你在使用表空間,你應該驗證pg_tblspc/中的符號連結被正確地恢復。

5.現在已經從備份中恢復了整個資料目錄,接下來,你需要部分或全部刪除資料目錄中的下列檔案,如果它們存在:

    (1) postmaster.pid;(必須)

    (2) pg_xlog中的檔案;(必須)

    (3) pgsql_tmp開頭的臨時檔案;(可選)

    (4) postgresql.auto.conf.tmp;(可選)

    (5) pg_replslot目錄中的檔案;(可選)

    (6) pg_stat_tmp目錄中的檔案。(可選)

6. 如果你有在第2步中儲存的未歸檔WAL段檔案,把它們拷貝到pg_xlog/中或WAL日誌的歸檔目錄中。

在集簇資料目錄中建立一個恢復命令檔案recovery.conf。如果你想恢復到最近的一致狀態,在recovery.conf寫入如下兩行:

restore_command = 'copy /y D:\\archive \\%f\\%p'

recovery_target_timeline = 'latest'

其中,restore_command的內容表示將歸檔日誌資料夾中的內容拷貝到pg_xlog,其引數的含義與上文archive_command的引數含義完全相同;recovery_target_timeline = 'latest' 表示恢復到最近的時間點。

        7. 如果要阻止普通使用者在成功恢復之前連線,請修改pg_hba.conf。

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

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

這樣就完成了一次檔案系統級別的全量備份,並實現了WAL檔案級別的增量備份。

 

附:pg_start_backup() pg_stop_backup()做了什麼?

       1. pg_start_backup()

        pg_start_backup() 的函式原型如下:

        pg_start_backup(label text [, fast boolean [, exclusive boolean ]])

        其中label是用來唯一標識這次備份操作的任意字串,fast 表示是否立即執行強制的檢查點,exclusive 表示該備份是否是一個排他備份。

        使用該函式時,推薦將exclusive設定為false,即非排他方式備份。

       執行下面的命令:

        Select pg_start_backup('backup_label', false, false);

        這條命令會產生三個動作:

         1. 在緩衝區中建立兩個變數:label_file和tblspc_map_file。前者包含WAL的起始位置、檢查點的起始位置、備份方法、備份模式、備份開始時間和備份標籤的名稱(本例中,名稱為“backup_label”)等資訊;後者包含 “pg_tblspc/”中表空間符號連結的資訊,如果它們存在。

        2. 強制發生一次檢查點。將檢查點開始前提交的事務對資料庫的修改重新整理到磁碟。

        3. 置寫日誌標誌為:XLogCtl->Insert.forcePageWrites = true。把這個標誌設定為true後,如果在備份期間時有其他事務修改資料庫,那麼系統會把被修改的資料頁在修改前的完整頁面都記錄到WAL中,而不僅僅是記錄頁面中的變化的部分。

        為什麼要將完整的頁面記錄到WAL中呢?

        原因是在備份過程中,其他事務修改資料庫,可能會造成備份後的資料存在新舊資料混合的情況,甚至同一資料頁面也會出現這種情況。假如WAL日誌中僅僅記錄變化的那部分的內容,就無法將資料庫恢復到上次備份結束時刻的狀態。所以要將修改後的完整頁面寫入WAL中,以保證資料庫恢復後的一致性。

 

2. pg_stop_backup()

pg_stop_backup()的函式原型如下:

pg_stop_backup([, exclusive boolean ]);

exclusive 表示該備份是否是一個排他備份。

如果採用非排他方式備份,執行:

Select pg_stop_backup(false);

這條命令會產生如下動作:

1. label_file和tblspc_map_file的內容會包含在該函式返回的結果中,並且應該被寫入到該備份的一些檔案中(這些內容不在資料目錄中)。

2. 在事務日誌歸檔區裡建立一個備份歷史檔案(.hostory)。這個歷史檔案包含 pg_start_backup的標籤、備份的起始與終止事務日誌位置以及備份的起始和終止時間。返回值是備份的終止事務日誌位置(同樣也可以被忽略)。 在記錄結束位置之後,當前事務日誌插入點被自動地推進到下一個事務日誌檔案,這樣結束的事務日誌檔案可以立即被歸檔來結束備份。

 

參考文獻

[1] PostgreSQL全球開發小組. PostgreSQL 9.6.0 文件. 連續歸檔和時間點恢復(PITR).

[2] 2ndquadrant. What does pg_start_backup() do?. 2017-1-23

[3] Michael Paquire. Postgres 9.6 feature highlight - Non-exclusive base backups. 2016-5-31