Hướng dẫn Backup và Restore PostgreSQL: Chọn Đúng Cách Cho Từng Tình Huống

Database tutorial - IT technology blog
Database tutorial - IT technology blog

Mình từng chứng kiến một team mất trắng 2 tuần dữ liệu vì backup PostgreSQL chạy mỗi đêm nhưng không ai kiểm tra file backup có thực sự restore được không. Đến khi server chết, mở file ra thì toàn bộ là 0 byte. Câu chuyện đó khiến mình cẩn thận hơn rất nhiều với chuyện backup database.

PostgreSQL có nhiều cách backup — và không phải cách nào cũng phù hợp cho mọi trường hợp. Cứ chọn bừa một kiểu rồi dùng mãi, đến lúc restore khẩn cấp mới phát hiện nó không đủ đáp ứng.

Ba Cách Backup PostgreSQL Phổ Biến Nhất

1. pg_dump — Backup logic từng database

pg_dump xuất dữ liệu ra SQL script hoặc custom format nén gọn. Muốn backup một database cụ thể mà không đụng đến các database khác? Đây là lựa chọn đơn giản nhất.

Ưu điểm:

  • Không cần dừng PostgreSQL, backup online hoàn toàn
  • File backup portable — có thể restore lên version PostgreSQL khác
  • Chọn được schema, table cụ thể nếu muốn
  • Tốt cho migrate giữa các server khác nhau

Nhược điểm:

  • Chậm với database lớn — database 50GB có thể mất 20-30 phút
  • Chỉ backup một thời điểm cố định — mất dữ liệu phát sinh sau đó nếu có sự cố
  • Không restore được từng transaction lẻ

2. pg_basebackup — Backup vật lý toàn cluster

pg_basebackup sao chép toàn bộ data directory — giống chụp ảnh nguyên xi thư mục data của PostgreSQL. Tool này thường xuất hiện trong hai kịch bản: setup streaming replication, hoặc production cần RTO thấp (restore trong vài phút thay vì vài giờ).

Ưu điểm:

  • Nhanh hơn pg_dump với database lớn
  • Có thể kết hợp WAL để Point-in-Time Recovery (PITR)
  • Dùng được để bootstrap streaming replication

Nhược điểm:

  • Chỉ restore được cùng phiên bản PostgreSQL (major version)
  • Không restore được từng database riêng lẻ
  • Cần cấu hình thêm nếu muốn PITR thực sự

3. WAL Archiving + PITR — Backup liên tục theo thời gian

PostgreSQL ghi mọi thay đổi vào Write-Ahead Log (WAL) — cứ coi đó như nhật ký chi tiết từng thao tác trên database. Lưu các WAL file này liên tục, bạn có thể tua ngược về bất kỳ thời điểm nào — kể cả 30 giây trước khi ai đó chạy DELETE FROM orders WHERE 1=1 nhầm.

Ưu điểm:

  • RPO (Recovery Point Objective) gần như bằng 0
  • Phù hợp cho production quan trọng, tài chính, y tế

Nhược điểm:

  • Cấu hình phức tạp hơn nhiều
  • Tốn không gian lưu trữ đáng kể
  • Cần công cụ như pgBackRest hoặc Barman để quản lý bài bản

Nên Chọn Cách Nào?

Khi migrate database 100GB từ MySQL sang PostgreSQL, mình mất 3 ngày planning và 1 ngày thực thi. Phần lớn thời gian là để quyết định chiến lược backup cho hệ thống mới. Cuối cùng dùng kết hợp: pg_dump hàng ngày cho backup logic, pg_basebackup + WAL archiving cho recovery nhanh khi cần.

Nguyên tắc chọn thực tế:

  • Database nhỏ hơn 10GB, môi trường dev/staging: pg_dump là đủ
  • Database lớn, production không quan trọng lắm: pg_basebackup định kỳ
  • Production quan trọng, không chịu được mất dữ liệu: WAL Archiving + PITR

Hướng Dẫn Triển Khai Thực Tế

Backup bằng pg_dump

Cú pháp cơ bản:

# Backup một database ra file SQL
pg_dump -U postgres -d mydb -f /backup/mydb_$(date +%Y%m%d).sql

# Backup ra custom format (nén, restore nhanh hơn)
pg_dump -U postgres -d mydb -Fc -f /backup/mydb_$(date +%Y%m%d).dump

# Backup nhiều database cùng lúc (toàn cluster)
pg_dumpall -U postgres -f /backup/all_$(date +%Y%m%d).sql

Script backup tự động hàng ngày với crontab:

#!/bin/bash
# /opt/scripts/pg_backup.sh

BACKUP_DIR="/backup/postgresql"
DATE=$(date +%Y%m%d_%H%M)
RETENTION_DAYS=7

mkdir -p "$BACKUP_DIR"

# Backup từng database
for DB in $(psql -U postgres -t -c "SELECT datname FROM pg_database WHERE datistemplate = false;"); do
  pg_dump -U postgres -Fc "$DB" -f "$BACKUP_DIR/${DB}_${DATE}.dump"
  echo "Backed up: $DB"
done

# Xóa backup cũ hơn 7 ngày
find "$BACKUP_DIR" -name "*.dump" -mtime +$RETENTION_DAYS -delete
echo "Cleanup done: removed backups older than $RETENTION_DAYS days"

Thêm vào crontab để chạy 2 giờ sáng mỗi ngày:

crontab -e
# Thêm dòng sau:
0 2 * * * /opt/scripts/pg_backup.sh >> /var/log/pg_backup.log 2>&1

Restore từ pg_dump

# Restore từ file SQL
psql -U postgres -d mydb -f /backup/mydb_20240215.sql

# Restore từ custom format (nhanh hơn, hỗ trợ parallel)
pg_restore -U postgres -d mydb -j 4 /backup/mydb_20240215.dump

# Nếu database chưa tồn tại, tạo trước
createdb -U postgres mydb_restored
pg_restore -U postgres -d mydb_restored /backup/mydb_20240215.dump

Backup vật lý bằng pg_basebackup

# Backup toàn bộ data directory
pg_basebackup -U replicator -h localhost -D /backup/basebackup_$(date +%Y%m%d) \
  -Ft -z -P -R

# Giải thích các flag:
# -Ft  : format tar
# -z   : nén gzip
# -P   : hiển thị tiến độ
# -R   : tạo file standby.signal (dùng cho replication)

Để pg_basebackup hoạt động, user replicator cần quyền replication. Thêm vào pg_hba.conf:

local   replication   replicator                md5

Và tạo user:

psql -U postgres -c "CREATE USER replicator REPLICATION LOGIN PASSWORD 'yourpassword';"

Kiểm Tra Backup Có Dùng Được Không

Đây chính là bước mà team mình nhắc ở đầu bài đã bỏ qua — và trả giá bằng 2 tuần dữ liệu. Định kỳ test restore trên staging, không thể lười:

# Tạo database test
createdb -U postgres mydb_test

# Thử restore
pg_restore -U postgres -d mydb_test /backup/mydb_latest.dump

# Kiểm tra số row một số bảng quan trọng
psql -U postgres -d mydb_test -c "SELECT COUNT(*) FROM users;"
psql -U postgres -d mydb_test -c "SELECT COUNT(*) FROM orders;"

# Xóa database test sau khi verify
dropdb -U postgres mydb_test

Script hóa bước này, chạy hàng tuần, gửi kết quả qua Slack. Không cần ai nhớ làm thủ công.

Một Vài Lưu Ý Thực Tế

  • Đừng backup vào cùng server chứa database — disk hỏng hoặc server cháy là mất cả đôi.
  • Encrypt backup nếu có dữ liệu nhạy cảm: gpg --symmetric backup.dump
  • Monitor dung lượng thư mục backup: disk đầy thì script chạy xong vẫn tạo file 0 byte, không ai biết cho đến khi cần restore.
  • Test restore ít nhất 1 lần/tháng — backup không test là backup không tồn tại.
  • Với PostgreSQL 15+, dùng pg_dump --compress=lz4 thay vì gzip — nhanh hơn 3-4x với CPU overhead thấp hơn đáng kể.

Backup nghe nhàm, nhưng đó là thứ duy nhất còn lại khi mọi thứ đổ bể. Pattern này lặp đi lặp lại: hệ thống chạy ổn 2-3 năm, một ngày disk hỏng hoặc ai đó xóa nhầm data, lúc ấy mới hỏi “backup ở đâu?”. Đừng để câu hỏi đó xuất hiện khi đã quá muộn.

Share: