1. 備份恢復
1.1 備份方式
備份方式分為物理備份和邏輯備份
- 物理備份:通過冗余數據文件提供數據保護,在文件系統對數據目錄,參數文件進行物理拷貝,復制到其他路徑或存儲設備中的方法稱為物理備份
- 邏輯備份:利用工具按照一定邏輯將數據庫對象導出到文件,需要時再利用工具把邏輯備份文件重新導人到數據庫中,除了利用工具導出,用 CREATE TABLE AS, COPY等 SQL 命令保存數據副本的方式也被認為是邏輯備份的一種 。 邏輯備份是數據庫對象級的備份
1.2 備份恢復目標衡量指標
根據自身業務情況和特點,對數據庫系統的備份和恢復制定完善的策略 。
RTO ( Recovery Time Objective )和 RPO (Recovery Point Objective )是規划備份和恢復策略時最重要的兩個衡量指標 。
-
RTO 是恢復時間目標:指從故障發生開始到業務系統恢復服務所需的時間
-
RPO 是恢復點目標:指故障發生后可以容忍丟失多少數據
1.3 PG支持的備份方法
有三種不同的基本方法來備份PostgreSQL數據:
- SQL轉儲
- 文件系統級備份
- 增量備份和基於時間點恢復(Point-in-time recovery,縮寫PITR)
2. SQL轉儲(邏輯備份恢復)
SQL 轉儲方法的思想是創建一個由SQL命令組成的文件,服務器將利用其中的SQL命令重建與轉儲時狀態一樣的數據庫。
- pg_dump:將單個數據庫抽取為一個腳本文件或其他歸檔文件,不會轉儲關於角色或表空間
- pg_dumpall: pg_dump+可以轉儲一個數據庫集簇的全部內容
2.1 pg_dump工具
2.1.1 備份
pg_dump [connection-option...] [option...] [dbname]
pg_dump是用於備份一種PostgreSQL數據庫的工具。即使數據庫正在被並發使用,它也能創建一致的備份。pg_dump不阻塞其他用戶訪問數據庫(讀取或寫入)。
pg_dump只轉儲單個數據庫。要備份一個集簇或者集簇中 對於所有數據庫公共的全局對象(例如角色和表空間),需要使用pg_dumpall
轉儲可以被輸出到腳本或歸檔文件格式。
- 腳本格式:SQL 命令的純文本文件,可以通過psql根據SQL命令重構數據庫到它被轉儲時的狀態
- 歸檔文件格式:必須要使用pg_restore工具恢復。最靈活的輸出文件格式是“自定義”格式(
-Fc)和“目錄”格式(-Fd)。它們允許選擇和重排序所有已歸檔項、支持並行恢復並且默認是壓縮的。“目錄”格式是唯一一種支持並行轉儲的格式。
pg_dump的優勢:
- pg_dump可以用其他格式創建文件以支持並行和細粒度的對象恢復控制。
- pg_dump的輸出可以很容易地在新版本的PostgreSQL中載入。
pg_dump 選項
| 選項 | 說明 |
|---|---|
| dbname | 指定要被轉儲的數據庫名 |
| -a|--data-only | 只轉儲數據,而不轉儲模式(數據定義)。表數據、大對象和序列值都會被轉儲。 |
| -b | --blobs | 轉儲中包括大對象。這是當--schema、--table或--schema-only被指定時的默認行為。 |
| -B | --no-blobs | 在轉儲中排除大對象 |
| -c | --clean | 在輸出創建數據庫對象的命令之前輸出刪除它們的命令 |
| -C | --create | 包含創建 Database 的命令 |
| -E encoding |--encoding=encoding | 指定的字符集編碼創建轉儲 |
| -f file | --file=file | 將輸出到指定文件 |
| -F format | --format=format | 選擇輸出的格式。format格式:[p|plain, c|custom, d|directory, t|tar] p: 輸出一個純文本形式的SQL腳本文件 c: 輸出適合於pg_restore輸入的自定義格式 d: 將表和其他對象輸出為文件,並保存在一個目錄中 t:輸出為 tar 包 |
| -j, --jobs=NUM | 並行度 |
| -n, --schema=PATTERN | 指定 Schema |
| -N, --exclude-schema=PATTERN | 排除指定的 Schema |
| -s, --schema-only | 指定轉儲包含Schema |
| -t, --table=PATTERN | 轉儲指定的表 |
| -T, --exclude-table=PATTERN | 排除指定的表 |
2.1.2 恢復
1)pg_dump生成的文本文件可以由psql工具支持SQL文件即可恢復。
psql dbname < dumpfile
# 默認情況下,psql腳本在遇到一個SQL錯誤后會繼續執行
# 設置ON_ERROR_STOP變量,讓它遇到錯誤時退出psql
psql --set ON_ERROR_STOP=on dbname < infile
# pg_dump和psql讀寫管道的能力使得直接從一個服務器轉儲一個數據庫到另一個服務器
pg_dump -h host1 dbname | psql -h host2 dbname
在執行恢復前,需要先創建數據庫dbname,對象擁有着及其相關授權用戶
pg_dump產生的轉儲是相對於template0。這意味着在template1中加入的任何語言、過程等都會被pg_dump轉儲。如果在恢復時使用的是一個自定義的template1,則必須從template0創建一個空的數據庫
2)轉儲格式自定義格式時,只能用pg_restore工具恢復
# 備份語法
pg_dump -Fc dbname > filename
# 恢復語法
pg_restore -d dbname filename
# 備份
pg_dump -Fc -p 1921 devdb > devdb.dat
# 恢復
pg_restore -p 1921 -d devdb devdb.dat
# 使用-j參數控制並行度
pg_dump -j num -F d -f out.dir dbname
# 恢復
# pg_restore -j來以並行方式恢復一個轉儲
2.2 pg_dumpall工具
pg_dump每次只轉儲一個數據庫,而且它不會轉儲關於角色或表空間(因為它們是集簇范圍的)的信息。為了支持方便地轉儲一個數據庫集簇的全部內容,提供了pg_dumpall程序。pg_dumpall備份一個給定集簇中的每一個數據庫,並且也保留了集簇范圍的數據,如角色和表空間定義。
# 轉儲
pg_dumpall > dumpfile
# 恢復
psql -f dumpfile postgres
恢復一個pg_dumpall轉儲時常常需要具有數據庫超級用戶訪問權限,因為它需要恢復角色和表空間信息。
pg_dumpall工作時會發出命令重新創建角色、表空間和空數據庫,接着為每一個數據庫pg_dump。這意味着每個數據庫自身是一致的,但是不同數據庫的快照並不同步。
集簇范圍的數據可以使用pg_dumpall的--globals-only選項來單獨轉儲。
2.3 處理大型數據庫
在一些具有最大文件尺寸限制的操作系統上創建大型的pg_dump輸出文件可能會出現問題。幸運地是,pg_dump可以寫出到標准輸出,因此你可以使用標准Unix工具來處理這種潛在的問題。
# 1.1 使用壓縮轉儲
pg_dump dbname | gzip > filename.gz
# 1.2 恢復
gunzip -c filename.gz | psql dbname
cat filename.gz | gunzip | psql dbname
# 2.1 使用split。 split命令允許你將輸出分割成較小的文件以便能夠適應底層文件系統的尺寸要求
pg_dump dbname | split -b 1m - filename
# 2.2 恢復
cat filename* | psql dbname
3. 文件系統級別備份
先停止數據庫,然后直接使用操作系統自帶命令復制PostgreSQL用於存儲數據庫中數據的文件到合適位置即可。
tar -cf backup.tar /usr/local/pgsql/data
rsync --checksum
4. 增量備份
從一個基礎備份時間開始的連續的歸檔WAL文檔序列。
4.1 開啟WAL歸檔
任何數據的修改首先寫入WAL日志,然后才對數據文件進行修改。PostgreSQL在數據集簇目錄的$PGDATA/pg_wal/子目錄下存儲預寫式日志(WAL)。
啟用WAL歸檔,需設置wal_level配置參數為replica或更高(級別:minimal<replica<logical),設置archive_mode為on,並且使用archive_command配置參數指定一個shell命令。在archive_command中,%p會被將要歸檔的文件路徑所替代,而%f只會被文件名所替代(路徑名是相對於當前工作目錄而言的,即集簇的數據目錄)
4.1.1 創建歸檔目錄
mkdir -p /ups/data/pgdata/12/arch_wal
chown postgres:postgres /ups/data/pgdata/12/arch_wal
4.1.2 修改wal_level參數
# 1.1 vi postgresql.conf
wal_level = 'replica'
# 1.2 SQL命令方式修改
psql -c "ALTER SYSTEM SET wal_level = 'replica';"
4.1.3 修改archive_mode參數
# 1.1 vi postgresql.conf
archive_mode = 'on'
# 1.2 SQL命令方式修改
psql -c "ALTER SYSTEM SET archive_mode = 'on';"
4.1.4 修改archive_command參數
# 1.1 vi postgresql.conf
archive_command = 'cp %p /ups/data/pgdata/12/arch_wal/%f'
# 1.2 SQL命令方式修改
psql -c "ALTER SYSTEM SET archive_command = 'cp %p /ups/data/pgdata/12/arch_wal/%f';"
4.1.5 壓縮的歸檔日志
適用壓縮歸檔wal日志文件
-- 使用gzip來壓縮歸檔文件
archive_command = 'gzip < %p > /var/lib/pgsql/archive/%f'
-- 使用gunzip恢復
restore_command = 'gunzip < /mnt/server/archivedir/%f > %p'
4.2 制作基礎備份
在較低的PostgreSQL版本中,使用pg_start_backup和pg_stop_backup這些低級API創建基礎備份,從PostgreSQL9.1版開始有了pg_basebackup實用程序,使得創建基礎備份更便捷,pg_basebackup用普通文件或創建tar包的方式進行基礎備份,它在內部也是使用pg_start_backup和pg_stop_backup低級命令。
4.2.1 使用低級API制作基礎備份
低級API備份類型有2種
- 非排它低級備份:允許其他並發備份運行
- 排它低級備份:只能在主節點上制作備份,並且不允許並發備份任務
低級API創建基礎備份步驟
-
執行pg_start_backup命令開始備份(超級用戶或具有該函數執行權限的用戶連接到數據庫服務)
- 判斷是否開始WAL歸檔
- 強制進入全頁寫模式(full_page_writes = on)
- 創建一個檢查點
- 排它基礎備份還會創建一個
$PGDATA/backup_label文件,該文件包含如下內容:- START WAL LOCATION: 0/A000028 (file 00000001000000000000000A)
- CHECKPOINT LOCATION: 0/A000060 # 記錄由命令創建的檢查點的 LSN 位置
- BACKUP METHOD: pg_start_backup # 基礎備份的方法,如:pg_start_backup 或pg_basebackup
- BACKUP FROM: master # 備份來源,指是從 master 或 standby 做的基礎備份
- START TIME: 2020-06-12 09:43:29 CST # 開始時間戳
- LABEL: bk1 # 指定的標簽
- START TIMELINE: 1 # 時間線
-
使用系統命令創建數據目錄副本
-
使用rsync、tar、cp、scp等命令都可以創建數據目錄的副本。在創建過程中可以排除pg_wal和pg_replslot目錄、postmaster.opts文件、postmaster.pid文件,這些目錄和文件對恢復並沒有幫助
-
# 隱藏因文件在備份期間被改變的觸發的警告消息 tar --warning=no-file-changed --warning=no-file-removed -
可以備份中忽略無關重要的文件,如下:
-
$PGDATA/pg_wal/*
-
目錄pg_dynshmem/、pg_notify/、pg_serial/、pg_snapshots/、pg_stat_tmp/和pg_subtrans/的內容(但不是這些目錄本身)可以從備份中省略,因為它們在postmaster啟動時會被初始化。
-
臨時統計數據的目錄(默認值:pg_stat_tmp)
-
任何以
pgsql_tmp開始的文件 -
關系緩沖數據(
pg_internal.init)文件
-
-
-
執行pg_stop_backup命令結束備份
-
恢復full_page_writes到之前的值
-
寫一個備份結束的XLOG記錄
-
切換WAL段文件
-
創建一個備份歷史文件(
$PGDATA/pg_wal/*.backup),該文件包含$PGDATA/backup_label文件內容及pg_stop_backup命令的時間戳-
START WAL LOCATION: 0/A000028 (file 00000001000000000000000A) STOP WAL LOCATION: 0/A001ED8 (file 00000001000000000000000A) CHECKPOINT LOCATION: 0/A000060 BACKUP METHOD: pg_start_backup BACKUP FROM: master START TIME: 2020-06-12 09:43:29 CST LABEL: bk1 START TIMELINE: 1 STOP TIME: 2020-06-12 09:58:27 CST STOP TIMELINE: 1
-
-
刪除
$PGDATA/backup_label文件
-
低級API函數使用
-- pg_start_backup 函數
10:09:24 [local]:5432 postgres@postgres=# \sf pg_start_backup
CREATE OR REPLACE FUNCTION pg_catalog.pg_start_backup(label text, fast boolean DEFAULT false, exclusive boolean DEFAULT true)
RETURNS pg_lsn
LANGUAGE internal
PARALLEL RESTRICTED STRICT
AS $function$pg_start_backup$function$
-- pg_start_backup 語法
pg_start_backup(label text [, fast boolean [, exclusive boolean ]])
/*
* label :參數是用戶定義的備份標簽字符串,一般使用備份文件名 加日 期 作為備份標簽。
* fast :參數默認值是 false,表示是否盡快開始備份
* exclusive :參數決定 pg_start_backup 是否開始一次排他基礎備份,【已經被廢棄】
*/
-- pg_stop_backup 語法
pg_stop_backup() -- 完成執行排他的在線備份
pg_stop_backup(exclusive boolean) -- 結束執行排他或者非排他的在線備份
低級API創建基礎備份過程
# 1. 執行pg_start_backup開始備份
psql -c "SELECT pg_start_backup('base', false, false);"
# 2. 創建數據目錄的副本
cd /ups/data/pgdata/backups
tar -czf base.tgz /ups/data/pgdata/12/pg_root /ups/data/pgdata/12/pg_usr --exclude=/ups/data/pgdata/12/pg_root/postmaster.pid --exclude=/ups/data/pgdata/12/pg_root/postmaster.opts --exclude=/ups/data/pgdata/12/pg_root/pg_snapshots --exclude=/ups/data/pgdata/12/pg_root/pg_stat_tmp --exclude=/ups/data/pgdata/12/pg_root/log/* --exclude=/ups/data/pgdata/12/pg_root/pg_dynshmem --exclude=/ups/data/pgdata/12/pg_root/pg_notify --exclude=/ups/data/pgdata/12/pg_root/pg_serial --exclude=/ups/data/pgdata/12/pg_root/pg_subtrans
# 3. 執行 pg_stop_backup 結束備份
psql -c "SELECT pg_stop_backup(false) ;"
4.2.2 pg_basebackup創建基礎備份
pg_basebackup用法
pg_basebackup takes a base backup of a running PostgreSQL server.
Usage:
pg_basebackup [OPTION]...
Options controlling the output:
-D, --pgdata=DIRECTORY receive base backup into directory
-F, --format=p|t output format (plain (default), tar)
-r, --max-rate=RATE maximum transfer rate to transfer data directory
(in kB/s, or use suffix "k" or "M")
-R, --write-recovery-conf
write configuration for replication
-T, --tablespace-mapping=OLDDIR=NEWDIR
relocate tablespace in OLDDIR to NEWDIR
--waldir=WALDIR location for the write-ahead log directory
-X, --wal-method=none|fetch|stream
include required WAL files with specified method
-z, --gzip compress tar output
-Z, --compress=0-9 compress tar output with given compression level
General options:
-c, --checkpoint=fast|spread
set fast or spread checkpointing
-C, --create-slot create replication slot
-l, --label=LABEL set backup label
-n, --no-clean do not clean up after errors
-N, --no-sync do not wait for changes to be written safely to disk
-P, --progress show progress information
-S, --slot=SLOTNAME replication slot to use
-v, --verbose output verbose messages
-V, --version output version information, then exit
--no-slot prevent creation of temporary replication slot
--no-verify-checksums
do not verify checksums
-?, --help show this help, then exit
Connection options:
-d, --dbname=CONNSTR connection string
-h, --host=HOSTNAME database server host or socket directory
-p, --port=PORT database server port number
-s, --status-interval=INTERVAL
time between status packets sent to server (in seconds)
-U, --username=NAME connect as specified database user
-w, --no-password never prompt for password
-W, --password force password prompt (should happen automatically)
示例
# pg_basebackup創建基礎備份
${PGHOME}/bin/pg_basebackup -Ft -Pv -Xf -z -Z5 -p 1921 -D /ups/data/pgdata/11/backups
4.3 腳本備份歸檔日志文件
archive_command腳本
使用腳本定義archive_command,並完成以下需求
archive_command = 'local_backup_script.sh "%p" "%f"'
- 將數據拷貝到安全的場外數據存儲
- 批處理WAL文件,這樣它們可以每三小時被傳輸一次,而不是一次一個
- 與其他備份和恢復軟件交互
- 與監控軟件交互以報告錯誤
5. 基於時間點和還原點的恢復
通過重做WAL日志可以將數據庫恢復到最近的時間點或指定時間點,還可以恢復到指定的還原點。
5.1 使用一個連續歸檔備份進行恢復步驟
- 停止服務器
- 將整個集簇數據目錄和表空間復制到一個臨時位置,至少要復制數據庫集簇的
pg_wal子目錄下文件存儲起來 - 移除所有位於集簇數據目錄和正在使用的表空間根目錄下的文件和子目錄
- 從文件系統備份中恢復數據庫文件(將基礎備份文件恢復數據庫文件),需要保證
pg_tblspc/中的符號鏈接被正確地恢復 - 移除
pg_wal/目錄中的任何文件,這些是來自於文件系統備份而不是當前日志,因此可以被忽略。 - 拷貝步驟2上未歸檔的WAL段文件到pg_wal
- 恢復postgresql.conf配置設置並創建
$PGDATA/recovery.signal文件 - 啟動服務
- 檢查數據庫內容,確保恢復到期望的狀態
5.2 時間線
無論何時當一次歸檔恢復完成,一個新的時間線被創建來標識恢復之后生成的WAL記錄序列。時間線ID號是WAL段文件名的一部分,因此一個新的時間線不會重寫由之前的時間線生成的WAL數據。實際上可以歸檔很多不同的時間線。
每次當一個新的時間線被創建,PostgreSQL會創建一個“時間線歷史”文件,它顯示了新時間線是什么時候從哪個時間線分支出來的。系統在從一個包含多個時間線的歸檔中恢復時,這些歷史文件對於允許系統選取正確的WAL段文件非常必要。
恢復的默認行為是沿着相同的時間線進行恢復,該時間線是基礎備份創建時的當前時間線。
查看時間線
pg_controldata $PGDATA |grep 'TimeLineID'
當數據庫初始化時,initdb 創建的原始數據目錄的 TimeLineID 為 1。 每進行一次恢復,TimeLineID 將加1。
[postgres@progs pg_root]$ pg_controldata $PGDATA |grep 'TimeLineID'
Latest checkpoint's TimeLineID: 1
Latest checkpoint's PrevTimeLineID: 1
時間線文件
每進行一次恢復操作后,創建一些時間線文件($PGDATA/pg_wal/*.history),這些時間線文件用於區分原始和恢復數據庫集簇。每個時間線都有一個相應的TimeLineID, TimeLinelD是一個從1開始的4字節無符號整數。時間線文件的命名格式是“TimeLineID.history”。如:當前的TimeLinelD為2,時間線文件的名即為00000002.history。
5.3 恢復案例
恢復點相關參數
#recovery_target = '' # 'immediate' to end recovery as soon as a
#recovery_target_name = '' # the named restore point to which recovery will proceed
#recovery_target_time = '' # the time stamp up to which recovery will proceed
#recovery_target_xid = '' # the transaction ID up to which recovery will proceed
#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed
#recovery_target_inclusive = on # Specifies whether to stop:
recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID
#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown'
測試數據
-- psql devdb dev
CREATE TABLE tb1(
id serial primary key,
ival INT NOT NULL DEFAULT 0,
description TEXT,
ctime TIMESTAMPTZ NOT NULL DEFAULT now()
);
INSERT INTO tb1 (ival) VALUES(1);
SELECT id, ival, description, ctime FROM tb1;
-- 切換WAL日志
SELECT pg_switch_wal() ;
創建基礎備份
${PGHOME}/bin/pg_basebackup -Ft -Pv -Xf -z -Z5 -p 5432 -D /ups/data/pgdata/12/backups
[postgres@progs pg_root]$ ${PGHOME}/bin/pg_basebackup -Ft -Pv -Xf -z -Z5 -p 5432 -D /ups/data/pgdata/12/backups
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/C000028 on timeline 1
104464/104464 kB (100%), 2/2 tablespaces
pg_basebackup: write-ahead log end point: 0/C000138
pg_basebackup: syncing data to disk ...
pg_basebackup: base backup completed
[postgres@progs pg_root]$ ls -l /ups/data/pgdata/12/backups
total 7456
-rw------- 1 postgres postgres 2871315 Jun 12 11:12 16384.tar.gz
-rw------- 1 postgres postgres 4756098 Jun 12 11:12 base.tar.gz
[postgres@progs pg_root]$
# 查看備份文件內容
[postgres@progs pg_root]$ tar -tf /ups/data/pgdata/12/backups/16384.tar.gz
PG_12_201909212/
PG_12_201909212/16385/
PG_12_201909212/16385/1255
PG_12_201909212/16385/1255_fsm
5.3.1 恢復到最近時間點
插入測試記錄
INSERT INTO tb1 (ival) VALUES(2);
SELECT id, ival, description, ctime FROM tb1;
移除故障數據庫目錄(先停服務)
[postgres@progs pg_root]$ pg_ctl -mi stop
waiting for server to shut down.... done
server stopped
[postgres@progs pg_root]$ rm -rf $PGDATA/pg_root
解壓基礎備份文件
tar -xf /ups/data/pgdata/12/backups/base.tar.gz -C /ups/data/pgdata/12/pg_root/
配置恢復參數
# 12+ 版本 postgresql.conf
# before 11版本 recovery.conf
restore_command = '/usr/bin/lz4 -d /ups/data/pgdata/12/archive_wals/%f.lz4 %p'
recovery_target_timeline = 'latest'
啟動服務開始恢復
pg_ctl -D /ups/data/pgdata/12/pg_root start
檢查數據是否恢復成功
-- psql devdb dev
SELECT id, ival, description, ctime FROM tb1;
5.3.2 恢復到指定時間點
在5.3.1章節中參數recovery_target_time指定時間點恢復即可,其它步驟一樣。
recovery_target_time = '2020-06-12 12:18:18.634808+08'
5.3.3 恢復到指定還原點
前提:基礎備份+還原點。通常用於重大業務發布場景。
創建還原點
-- psql
\df pg_create_restore_point
List of functions
Schema | Name | Result data type | Argument data types | Type
------------+-------------------------+------------------+---------------------+------
pg_catalog | pg_create_restore_point | pg_lsn | text | func
-- 創建還原點
SELECT pg_create_restore_point('restore_point');
恢復過程
恢復過程跟上面章節內容一致,只需移除參數recovery_target_timeline,並將recovery_target_name配置成'restore_point'后進行恢復即可
recovery_target_name = 'restore_point'
5.3.4 恢復到指定事務
主要知道待恢復的事務ID號,配置recovery_target_xid參數值進行恢復即可
recovery_target_xid = 3385
5.3.5 恢復到指定時間線
只需配置參數,其它恢復過程都一致
# 12+ 版本 postgresql.conf
# before 11版本 recovery.conf
restore_command = '/usr/bin/lz4 -d /ups/data/pgdata/12/archive_wals/%f.lz4 %p'
recovery_target_timeline = 2
recovery_target_time = '2020-06-12 12:18:18.634808+08'
