Chuyển đến nội dung chính

備份和還原 PostgreSQL 綜合指南

Duy Tran13 分鐘
備份和還原 PostgreSQL 綜合指南

為什麼備份很重要?

在討論技術細節之前,讓我們先回顧一個真實的故事:越南的一家科技新創公司曾經因為沒有適當的備份而失去了所有客戶資料。結果?他們花了 6 個月的時間才恢復了客戶的信任,差點破產。

備份不僅是一項技術工作,更是保護您業務的策略。

第 1 部分:PostgreSQL 備份方法

1.1.使用 pg_dump 進行邏輯備份

這是最常用的方法,適用於大多數情況。

備份單一資料庫

# Format SQL plain text (dễ đọc, dễ edit)
pg_dump -U postgres -d myapp_db > myapp_backup.sql

Format custom (nén tốt, restore linh hoạt)

pg_dump -U postgres -d myapp_db -F c -f myapp_backup.dump

Format directory (backup song song, nhanh nhất)

pg_dump -U postgres -d myapp_db -F d -j 4 -f myapp_backup_dir/

比較格式:

格式 優點 缺點 何時使用
普通 SQL (-F p) 易於閱讀和編輯 無壓縮,恢復速度慢 資料庫較小,需要查看內容
自訂 (-F c) 良好的壓縮,選擇性恢復 無法直接讀取 大多數情況
目錄 (-F d) 並行備份,最快 需要很多文件 大型資料庫(>100GB)
焦油 (-F t) 可壓縮 不要並行恢復 很少使用

選擇性備份

# Chỉ backup schema (cấu trúc tables, indexes, constraints)
pg_dump -U postgres -d myapp_db --schema-only > schema.sql

Chỉ backup data

pg_dump -U postgres -d myapp_db --data-only > data.sql

Backup một table cụ thể

pg_dump -U postgres -d myapp_db -t users -t orders > important_tables.sql

Backup tất cả trừ table logs (thường rất lớn)

pg_dump -U postgres -d myapp_db -T logs > backup_no_logs.sql

Backup theo schema

pg_dump -U postgres -d myapp_db -n public -n reporting > selected_schemas.sql

實例:備份資料庫製作

#!/bin/bash

Script: backup_production.sh

DB_NAME="myapp_production" DB_USER="postgres" DB_HOST="localhost" BACKUP_DIR="/backups/postgresql" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.dump"

Tạo thư mục nếu chưa có

mkdir -p $BACKUP_DIR

Backup với compression level 9

pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME
-F c -Z 9
-f $BACKUP_FILE
--verbose

Check kết quả

if [ $? -eq 0 ]; then echo "✓ Backup thành công: $BACKUP_FILE" SIZE=$(du -h $BACKUP_FILE | cut -f1) echo "✓ Kích thước: $SIZE" else echo "✗ Backup thất bại!" exit 1 fi

Xóa backup cũ hơn 7 ngày

find $BACKUP_DIR -name "${DB_NAME}_*.dump" -mtime +7 -delete

echo "✓ Đã xóa backup cũ hơn 7 ngày"

1.2.使用pg_dumpall備份整個集群

當您需要備份所有資料庫、角色和表空間時:

# Backup toàn bộ
pg_dumpall -U postgres > all_databases.sql

Chỉ backup global objects (roles, tablespaces)

pg_dumpall -U postgres --globals-only > globals.sql

Chỉ backup roles

pg_dumpall -U postgres --roles-only > roles.sql

何時使用 pg_dumpall?

  • 將整個 PostgreSQL 伺服器遷移到新機器時
  • 當您需要同時備份使用者權限和角色時
  • 當有多個資料庫相互關聯時

1.3.實體備份與基礎備份

這是一種檔案系統層級的備份方法,適用於非常大的資料庫。

# Bước 1: Tạo base backup
pg_basebackup -U postgres -D /backups/base -F tar -z -P

Bước 2: Configure WAL archiving (trong postgresql.conf)

wal_level = replica archive_mode = on archive_command = 'test ! -f /backups/wal/%f && cp %p /backups/wal/%f' max_wal_senders = 3

優點:

  • 對於大型資料庫(TB 級)非常快
  • 時間點恢復 (PITR) 支持
  • 可用於複製

缺點:

  • 比邏輯備份更複雜
  • 必須備份整個集群,不能選擇單一資料庫
  • 復原時需要相同的 PostgreSQL 版本

第 2 部分:復原 PostgreSQL

2.1.從 SQL 檔案恢復

# Tạo database mới (nếu cần)
createdb -U postgres myapp_db_restored

Restore từ SQL file

psql -U postgres -d myapp_db_restored < myapp_backup.sql

Restore với error handling

psql -U postgres -d myapp_db_restored
-v ON_ERROR_STOP=1
--echo-errors
< myapp_backup.sql

2.2.從自訂/目錄格式恢復

# Restore cơ bản
pg_restore -U postgres -d myapp_db myapp_backup.dump

Restore với clean (xóa objects cũ trước)

pg_restore -U postgres -d myapp_db -c myapp_backup.dump

Restore song song (nhanh hơn nhiều)

pg_restore -U postgres -d myapp_db -j 4 myapp_backup.dump

Restore chỉ một table

pg_restore -U postgres -d myapp_db -t users myapp_backup.dump

Restore vào database mới

pg_restore -U postgres -d postgres -C myapp_backup.dump

2.3.恢復完整腳本

#!/bin/bash

Script: restore_database.sh

BACKUP_FILE=$1 NEW_DB_NAME=$2

if [ -z "$BACKUP_FILE" ] || [ -z "$NEW_DB_NAME" ]; then echo "Usage: $0 <backup_file> <new_db_name>" exit 1 fi

echo "→ Kiểm tra file backup..." if [ ! -f "$BACKUP_FILE" ]; then echo "✗ File không tồn tại: $BACKUP_FILE" exit 1 fi

echo "→ Tạo database mới: $NEW_DB_NAME" createdb -U postgres $NEW_DB_NAME

if [ $? -ne 0 ]; then echo "✗ Không thể tạo database" exit 1 fi

echo "→ Đang restore..." pg_restore -U postgres -d $NEW_DB_NAME -j 4 --verbose $BACKUP_FILE

if [ $? -eq 0 ]; then echo "✓ Restore thành công!" echo "→ Thông tin database:" psql -U postgres -d $NEW_DB_NAME -c "\dt" else echo "✗ Restore thất bại!" exit 1 fi

第 3 部分:策略與最佳實踐

3.1.備份策略3-2-1

這是備份的黃金法則:

  • 3 資料的副本
  • 2 不同的媒體類型(例如磁碟和雲端)
  • 1 異地版本(在另一個實體位置)

實現範例:

#!/bin/bash

Script: backup_strategy_321.sh

DB_NAME="myapp_production" LOCAL_BACKUP="/backups/local" NAS_BACKUP="/mnt/nas/backups" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_NAME="${DB_NAME}_${DATE}.dump"

Backup 1: Local disk

echo "→ Creating local backup..." pg_dump -U postgres -d $DB_NAME -F c -f "$LOCAL_BACKUP/$BACKUP_NAME"

Backup 2: NAS (different media)

echo "→ Copying to NAS..." cp "$LOCAL_BACKUP/$BACKUP_NAME" "$NAS_BACKUP/"

Backup 3: Cloud (offsite) - AWS S3

echo "→ Uploading to S3..." aws s3 cp "$LOCAL_BACKUP/$BACKUP_NAME"
s3://mycompany-backups/postgresql/
--storage-class STANDARD_IA

echo "✓ 3-2-1 backup completed!"

3.2.備份計劃

針對不同環境的建議備份計畫:

發展:

# Crontab: Backup hàng ngày lúc 2 giờ sáng
0 2 * * * /scripts/backup_dev.sh

分期:

# Backup mỗi 6 giờ
0 */6 * * * /scripts/backup_staging.sh

生產:

# Full backup: Mỗi ngày lúc 2 giờ sáng
0 2 * * * /scripts/full_backup_prod.sh

Incremental backup: Mỗi giờ

0 * * * * /scripts/incremental_backup_prod.sh

WAL archiving: Continuous

3.3.使用 systemd 計時器實現自動化

# /etc/systemd/system/postgresql-backup.service
[Unit]
Description=PostgreSQL Backup Service
After=postgresql.service

[Service] Type=oneshot User=postgres ExecStart=/usr/local/bin/backup_postgres.sh StandardOutput=journal StandardError=journal

# /etc/systemd/system/postgresql-backup.timer
[Unit]
Description=PostgreSQL Daily Backup Timer

[Timer] OnCalendar=daily OnCalendar=02:00 Persistent=true

[Install] WantedBy=timers.target

啟用定時器:

sudo systemctl enable postgresql-backup.timer
sudo systemctl start postgresql-backup.timer

3.4.監控和警報

檢查備份的腳本:

#!/bin/bash

Script: check_backup_health.sh

BACKUP_DIR="/backups/postgresql" MAX_AGE_HOURS=24 SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

Tìm backup mới nhất

LATEST_BACKUP=$(find $BACKUP_DIR -name "*.dump" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" ")

if [ -z "$LATEST_BACKUP" ]; then MESSAGE="⚠️ CẢNH BÁO: Không tìm thấy backup nào!" curl -X POST -H 'Content-type: application/json'
--data "{"text":"$MESSAGE"}"
$SLACK_WEBHOOK exit 1 fi

Kiểm tra tuổi của backup

BACKUP_TIME=$(stat -c %Y "$LATEST_BACKUP") CURRENT_TIME=$(date +%s) AGE_HOURS=$(( ($CURRENT_TIME - $BACKUP_TIME) / 3600 ))

if [ $AGE_HOURS -gt $MAX_AGE_HOURS ]; then MESSAGE="⚠️ CẢNH BÁO: Backup quá cũ! Age: ${AGE_HOURS}h\nFile: $LATEST_BACKUP" curl -X POST -H 'Content-type: application/json'
--data "{"text":"$MESSAGE"}"
$SLACK_WEBHOOK exit 1 fi

Kiểm tra kích thước backup (phải > 0)

SIZE=$(stat -c %s "$LATEST_BACKUP") if [ $SIZE -eq 0 ]; then MESSAGE="⚠️ CẢNH BÁO: Backup có kích thước 0 bytes!\nFile: $LATEST_BACKUP" curl -X POST -H 'Content-type: application/json'
--data "{"text":"$MESSAGE"}"
$SLACK_WEBHOOK exit 1 fi

echo "✓ Backup health check passed" echo " Latest: $LATEST_BACKUP" echo " Age: ${AGE_HOURS}h" echo " Size: $(du -h $LATEST_BACKUP | cut -f1)"

3.5.測試恢復

重要: 如果您從未測試過恢復,備份就沒有價值!

#!/bin/bash

Script: test_restore.sh

BACKUP_FILE=$1 TEST_DB="test_restore_$(date +%s)"

echo "→ Testing restore from: $BACKUP_FILE"

Tạo test database

createdb -U postgres $TEST_DB

Restore

pg_restore -U postgres -d $TEST_DB $BACKUP_FILE

Kiểm tra

TABLES=$(psql -U postgres -d $TEST_DB -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public'")

if [ $TABLES -gt 0 ]; then echo "✓ Restore test PASSED: $TABLES tables restored"

# Verify data
psql -U postgres -d $TEST_DB -c "SELECT tablename, n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC LIMIT 10"

else echo "✗ Restore test FAILED: No tables found" fi

Cleanup

dropdb -U postgres $TEST_DB

echo "✓ Test completed and cleaned up"

第 4 部分:故障排除

4.1.備份時常見錯誤

錯誤:“權限被拒絕”

# Solution: Chạy với user có quyền
sudo -u postgres pg_dump mydb > backup.sql

Hoặc grant quyền

GRANT CONNECT ON DATABASE mydb TO backup_user; GRANT SELECT ON ALL TABLES IN SCHEMA public TO backup_user;

錯誤:“無法連接到伺服器”

# Kiểm tra PostgreSQL đang chạy
sudo systemctl status postgresql

Kiểm tra port

netstat -tlnp | grep 5432

Kiểm tra pg_hba.conf

sudo vim /etc/postgresql/14/main/pg_hba.conf

錯誤:備份時間太長

# Solution: Dùng format directory với parallel
pg_dump -F d -j 8 -f backup_dir/ mydb

Hoặc exclude tables lớn

pg_dump -T large_log_table mydb > backup.sql

4.2.恢復時常見錯誤

錯誤:“資料庫已存在”

# Solution 1: Drop database cũ
dropdb mydb
createdb mydb
pg_restore -d mydb backup.dump

Solution 2: Dùng flag -c (clean)

pg_restore -c -d mydb backup.dump

錯誤:“角色不存在”

# Solution: Restore roles trước
pg_dumpall --roles-only > roles.sql
psql -f roles.sql

Sau đó restore data

pg_restore -d mydb backup.dump

錯誤:磁碟空間不足

# Check disk space trước khi restore
df -h

Ước tính kích thước cần thiết (thường 2-3x backup file)

du -h backup.dump

第 5 部分:進階主題

5.1.時間點恢復 (PITR)

PITR 可讓您將資料庫還原到過去的特定時間。

設定 WAL 歸檔:

# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /wal_archive/%f && cp %p /wal_archive/%f'
archive_timeout = 300  # Force WAL rotation every 5 minutes

實施 PITR:

# Bước 1: Stop PostgreSQL
sudo systemctl stop postgresql

Bước 2: Restore base backup

rm -rf /var/lib/postgresql/14/main/* tar -xzf /backups/base.tar.gz -C /var/lib/postgresql/14/main/

Bước 3: Tạo recovery.conf (PostgreSQL < 12) hoặc recovery.signal (>= 12)

cat > /var/lib/postgresql/14/main/recovery.signal << EOF restore_command = 'cp /wal_archive/%f %p' recovery_target_time = '2024-01-15 14:30:00' recovery_target_action = 'promote' EOF

Bước 4: Start PostgreSQL

sudo systemctl start postgresql

5.2.使用 pgBackRest 進行連續歸檔

pgBackRest 是 PostgreSQL 的企業級備份工具:

# Cài đặt
sudo apt-get install pgbackrest

Config /etc/pgbackrest/pgbackrest.conf

[global] repo1-path=/var/lib/pgbackrest repo1-retention-full=2

[mydb] pg1-path=/var/lib/postgresql/14/main

Full backup

pgbackrest --stanza=mydb --type=full backup

Incremental backup

pgbackrest --stanza=mydb --type=incr backup

Restore

pgbackrest --stanza=mydb restore

5.3.使用 Docker 進行備份

# Backup PostgreSQL trong Docker
docker exec my_postgres_container pg_dump -U postgres mydb > backup.sql

Restore

docker exec -i my_postgres_container psql -U postgres mydb < backup.sql

Docker Compose với automated backups

version: '3.8' services: postgres: image: postgres:14 volumes: - postgres_data:/var/lib/postgresql/data

backup: image: prodrigestivill/postgres-backup-local environment: POSTGRES_HOST: postgres POSTGRES_DB: mydb POSTGRES_USER: postgres POSTGRES_PASSWORD: password SCHEDULE: "@daily" volumes: - ./backups:/backups

結論

備份和復原不僅是一項簡單的技術任務,也是企業資料保護策略的重要組成部分。需要記住的一些要點:

  1. 始終測試恢復 - 備份未測試 = 無備份
  2. 自動化一切 - 不要依賴記住運行手動備份
  3. 遵循 3-2-1 規則 - 3 份,2 種媒體類型,1 份異地
  4. 監控和警報 - 出現問題時立即知道
  5. 記錄一切 - 新團隊也必須懂得如何恢復

最終清單

  • [ ] 備份腳本已編寫並測試
  • [ ] crontab/systemd 定時器已設定
  • [ ] 監控警報已配置
  • [ ] 恢復已至少測試過一次
  • [ ] 文檔已經寫好
  • [ ] 團隊已接受備份/復原流程的培訓
  • [ ] 設定異地備份
  • [ ] 保留政策已明確定義

參考來源:

本文最後更新時間:2024 年 12 月