PostgreSQL 簡單備份的筆記
目標:當資料庫完全損毀時(例如 RAID 掛了),能夠靠備份完全回復到最少十分鐘前的資料。
規畫:
- Ubuntu 7.04
- PostgreSQL 8.2
- 每天做一次整個資料庫的完整備份
- 每十分鐘做一次異動的備份
- 資料庫本身是放在 RAID1 上,而備份則是放到外部的網路硬碟
- 每天自動清除過期的異動備份
PostgreSQL 的異動備份最小的 size 是 16MB,不論異動量是多少。按上面的規畫每10 分鐘就備份一次,換算下來,一整天最少就有 16MB * 6 * 24 = 2.3GB 的備份量。如果你的資料相當重要,那間格要壓到 1 分鐘或甚至 30 秒以下,光一天的備份就會爆增到 20GB 以上。
備份的步驟
- 資料目錄:
來源 備份路徑 完整的資料庫 /var/lib/postgresql/8.2/main /backup/main_sync 異動紀錄 (WAL, write ahead log) /var/lib/postgresql/8.2/main/pg_xlog /backup/archivedir
- 開啟 posgresql 自動連續備份的機制:請修改 /etc/postgresql/8.2/main/postgresql.conf
# 異動備份用 cp 複製到 /backup/archivedir 這個目錄裡 archive_command = 'test ! -f /backup/archivedir/%f && cp %p /backup/archivedir/%f' # 設定 600 秒(10分鐘) 備份一次 archive_timeout = 600
- 重新啟動 Postgresql (
/etc/init.d/postgresql-8.2 restart
) 後,postgresql 便會開始不斷產生 WAL 檔到 pg_xlog 裡。然後每十分鐘會執行上面設定的 archive_command 將 WAL 檔移動到 /backup/archivedir 裡 (先複製再刪除)。 - 進行完整的資料庫備份 (用 postgres 這個 unix 使用者執行):
- psql -ec "SELECT pg_start_backup('complete_backup');"
- rsync -av --exclude="pg_xlog" /var/lib/postgresql/8.2/main/ /backup/main_sync
- psql -ec "SELECT pg_stop_backup();"
完整的備份要使用 pg_start_backup() 和 pg_stop_backup() 兩個函數標示備份的期間,而實體的資料庫備份則是用 rsync 直接同步兩個目錄 (除了 pg_xlog 之外)。待 pg_stop_backup() 執行完後,postgresql 會馬上產生一個新的 WAL 檔,並且將該 WAL 視為下次重建資料的起點。 - 上述三步驟的範例 script:
continuous_archive.sh
#!/bin/bash backup_dir="/backup" master_backup="$backup_dir/main_sync" mailto="myadmin_mail@gmail.com" pg_version="8.2" started=false function exitIfFail() { ## $? is last command's exit status if [ $? -ne 0 ]; then echo 'archive failed' | mutt -s 'continue archive failed' $mailto # if backup started, we should force stop backup if [ started ]; then psql -ec "SELECT pg_stop_backup();" fi exit $? ; fi } ## initialize backup psql -ec "SELECT pg_start_backup('$master_backup');" exitIfFail; started=true ## master backup rsync -av --exclude="pg_xlog" /var/lib/postgresql/$pg_version/main/ $master_backup exitIfFail; ## create blank dir because we don't sync pg_xlog dir mkdir -p $master_backup/pg_xlog/archive_status ## finalized backup psql -ec "SELECT pg_stop_backup();" exitIfFail; started=false ## flush sync; purge_ts_file=$backup_dir/purge_ts_file ## delete fews days ago WAL in archivedir if [ -f $purge_ts_file ]; then find $backup_dir/archivedir ! -newer $purge_ts_file -type f -delete exitIfFail; fi ## udpate next purge date, though it's 2 days ago, we will use this file next day ## so actually is deleting 3 days old files purge_date="$(date --date='2 days ago' +'%m%d%H%M.%S')" touch -t $purge_date $backup_dir/purge_ts_file exit 0
上面的 script 的後半部是清除過期 (三天前) 的異動備份。而備份過程中發生任何錯誤則直接中斷,並使用 mutt 寄信通知。將此 script 以 crontab 設定每日執行即可。 - 接下來檢查 /backup/archivedir 和 /var/log/postgresql 裡的 log 看有沒有異狀就 ok 了。
重建的步驟
- 關閉 postgresql server (
/etc/init.d/postgresql-8.2 stop
) - 將損壞的資料庫移到別處 (
mv /var/lib/postgresql/8.2/main /temp
) - 複製完整的備份 /backup/main_sync 回 /var/lib/postgresql/8.2/main
- 建立空白目錄 /var/lib/postgresql/8.2/main/pg_xlog/archive_status
- 建立 /var/lib/postgresql/8.2/main/recovery.conf 內容為:
# 指定回復時到 /backup/archivedir 裡複製異動 log restore_command = 'cp /backup/archivedir/%f %p'
- 啟動 postgresql server,server 會使用 restore_command 複製異動 log,並自動重建資料。檢查 /var/log/postgresql 的 log 看有無異常。全部完成後 recovery.conf 會被改名為 recovery.done
- 移除已經完成重建的異動備份 (mv /backup/archivedir/* /temp)
- 手動重建 index (因為目前 8.2 版的 WAL 不包含 index 的內容)
psql -U mydbuser mydb -ec "reindex mydb"
- 檢查一切都 ok 後,移開之前的備份,從新設定備份的程序。
參考資料:Postgresql 手冊