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

Hướng Dẫn Toàn Diện về Backup và Restore PostgreSQL

Bài viết này sẽ giúp bạn nắm vững các phương pháp backup và restore PostgreSQL từ cơ bản đến nâng cao, kèm theo các best practices trong thực tế.

Hướng Dẫn Toàn Diện về Backup và Restore PostgreSQL

Tại Sao Backup Quan Trọng?

Trước khi đi vào chi tiết kỹ thuật, hãy cùng nhìn lại một câu chuyện có thật: Một startup công nghệ tại Việt Nam từng mất toàn bộ dữ liệu khách hàng vì không có backup đúng cách. Hệ quả? Họ mất 6 tháng để phục hồi niềm tin từ khách hàng và gần như phá sản.

Backup không chỉ là một công việc kỹ thuật, mà là một chiến lược bảo vệ doanh nghiệp của bạn.

Phần 1: Các Phương Pháp Backup PostgreSQL

1.1. Logical Backup với pg_dump

Đây là phương pháp phổ biến nhất, phù hợp cho hầu hết các trường hợp.

Backup một database đơn lẻ

# 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/

So sánh các format:

Format Ưu điểm Nhược điểm Khi nào dùng
Plain SQL (-F p) Dễ đọc, edit được Không nén, restore chậm Database nhỏ, cần xem nội dung
Custom (-F c) Nén tốt, restore chọn lọc Không đọc được trực tiếp Hầu hết các trường hợp
Directory (-F d) Backup song song, nhanh nhất Tốn nhiều files Database lớn (>100GB)
Tar (-F t) Nén được Không restore song song Ít dùng

Backup có chọn lọc

# 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

Ví dụ thực tế: Backup database production

#!/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. Backup toàn bộ cluster với pg_dumpall

Khi bạn cần backup tất cả databases, roles, và tablespaces:

# 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

Khi nào dùng pg_dumpall?

  • Khi migrate toàn bộ PostgreSQL server sang máy mới
  • Khi cần backup cả user permissions và roles
  • Khi có nhiều databases liên quan đến nhau

1.3. Physical Backup với Base Backup

Đây là phương pháp backup ở mức file system, phù hợp cho databases rất lớn.

# 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

Ưu điểm:

  • Rất nhanh cho database lớn (TB-level)
  • Hỗ trợ Point-in-Time Recovery (PITR)
  • Có thể dùng cho replication

Nhược điểm:

  • Phức tạp hơn logical backup
  • Phải backup cả cluster, không thể chọn database riêng lẻ
  • Yêu cầu cùng phiên bản PostgreSQL khi restore

Phần 2: Restore PostgreSQL

2.1. Restore từ SQL file

# 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 từ Custom/Directory format

# 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. Restore Script hoàn chỉnh

#!/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

Phần 3: Strategies và Best Practices

3.1. Chiến lược Backup 3-2-1

Đây là quy tắc vàng trong backup:

  • 3 bản copies của dữ liệu
  • 2 loại media khác nhau (ví dụ: disk và cloud)
  • 1 bản offsite (ở vị trí vật lý khác)

Ví dụ implementation:

#!/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. Backup Schedule

Đề xuất lịch backup cho các môi trường khác nhau:

Development:

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

Staging:

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

Production:

# 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. Tự động hóa với systemd timer

# /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

Enable timer:

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

3.4. Monitoring và Alerting

Script kiểm tra backup:

#!/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. Testing Restore

QUAN TRỌNG: Backup không có giá trị nếu bạn chưa bao giờ test restore!

#!/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"

Phần 4: Troubleshooting

4.1. Lỗi thường gặp khi Backup

Lỗi: "permission denied"

# 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;

Lỗi: "could not connect to server"

# 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

Lỗi: Backup quá lâu

# 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. Lỗi thường gặp khi Restore

Lỗi: "database already exists"

# 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

Lỗi: "role does not exist"

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

Sau đó restore data

pg_restore -d mydb backup.dump

Lỗi: Out of disk space

# 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

Phần 5: Advanced Topics

5.1. Point-in-Time Recovery (PITR)

PITR cho phép bạn restore database về một thời điểm cụ thể trong quá khứ.

Setup WAL archiving:

# 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

Thực hiện 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. Continuous Archiving với pgBackRest

pgBackRest là công cụ backup enterprise-grade cho 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. Backup với 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

Kết Luận

Backup và restore không chỉ là một task kỹ thuật đơn thuần, mà là một phần quan trọng trong chiến lược bảo vệ dữ liệu của doanh nghiệp. Một số điểm quan trọng cần nhớ:

  1. Luôn test restore - Backup chưa test = không có backup
  2. Automate everything - Đừng dựa vào việc nhớ chạy backup thủ công
  3. Follow 3-2-1 rule - 3 copies, 2 media types, 1 offsite
  4. Monitor và alert - Biết ngay khi có vấn đề
  5. Document everything - Team mới cũng phải biết cách restore

Checklist cuối cùng

  • [ ] Backup script đã được viết và test
  • [ ] Crontab/systemd timer đã được setup
  • [ ] Monitoring và alerting đã được cấu hình
  • [ ] Restore đã được test ít nhất 1 lần
  • [ ] Documentation đã được viết
  • [ ] Team đã được training về quy trình backup/restore
  • [ ] Offsite backup đã được setup
  • [ ] Retention policy đã được định nghĩa rõ ràng

Nguồn tham khảo:

Bài viết này được cập nhật lần cuối: Tháng 12, 2024

DUY TRAN
Tác giả

DUY TRAN

Pursuing an AI-first mindset and intelligent system architecture. I build solutions by combining technology, creativity, and the ability to see structure in chaos — the foundation for becoming a Solution Architect.

Bình luận

Bài viết liên quan

Circuit Breaker Pattern trong Spring Boot
DevOps

Circuit Breaker Pattern trong Spring Boot

Tìm hiểu Circuit Breaker Pattern trong Spring Boot với Resilience4j - từ nguyên lý hoạt động, cấu hình chi tiết, ví dụ thực tế đến best practices. Hướng dẫn đầy đủ giúp bạn xây dựng hệ thống microservices resilient, ngăn chặn cascading failures và tự động recovery khi service gặp sự cố.

Duy TranDuy Tran35 phút