Database Backup & Recovery: Xây Dựng Chiến Lược Vững Chắc Dưới Áp Lực

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

Khi Backup Thất Bại Vào Lúc Tệ Nhất

Một job backup chạy không có lỗi mỗi đêm tạo cảm giác được bảo vệ. Cho đến ngày bạn thực sự cần dùng đến nó. File dump bị hỏng. Hoặc quá trình restore mất sáu tiếng trên một database bạn kỳ vọng phục hồi trong ba mươi phút. Hoặc bản backup sạch gần nhất đã bốn ngày tuổi, vì job đã âm thầm thất bại và không ai để ý.

Không có gì là xui xẻo ở đây. Đây là kết quả hoàn toàn có thể đoán trước khi coi backup là đích đến thay vì recovery. Hầu hết các team thiết kế lịch chạy cẩn thận, cài đặt xong rồi để đó. Không ai chạy thử restore. Nguyên nhân gốc rễ rất đơn giản: team sở hữu backup. Không ai sở hữu recovery.

So Sánh Các Phương Pháp Backup

Không có chiến lược nào phù hợp với mọi hệ thống. Mỗi phương pháp đánh đổi khác nhau giữa tốc độ, độ chi tiết khi recovery, chi phí lưu trữ và độ phức tạp vận hành. Để kết hợp đúng, trước tiên phải hiểu rõ mình đang lựa chọn giữa những gì.

Logical Dump (pg_dump, mysqldump)

Điểm khởi đầu phổ biến nhất — và có lý do chính đáng. Logical dump xuất dữ liệu dưới dạng câu lệnh SQL hoặc định dạng binary di động. Dễ đọc, di chuyển được qua các phiên bản chính, dễ tự động hóa chỉ bằng một lệnh duy nhất.

# Tạo một bản dump PostgreSQL đã nén
pg_dump -U postgres -d mydb -Fc -f mydb_backup.dump

# Khôi phục về database mới
createdb mydb_restored
pg_restore -U postgres -d mydb_restored mydb_backup.dump

Vấn đề: dump rất chậm ở quy mô lớn. Một database PostgreSQL 300GB mất 3–4 tiếng để dump và thường lâu hơn để restore. Chúng cũng không hỗ trợ point-in-time recovery — bạn chỉ có thể quay lại đúng thời điểm dump chạy. Ổn với hệ thống nhỏ, nhưng là lỗ hổng thực sự với hệ thống production lưu lượng cao.

Physical Backup (pg_basebackup, Percona XtraBackup)

Thay vì xuất SQL, physical backup sao chép trực tiếp thư mục dữ liệu thô. Database PostgreSQL 300GB đó chỉ mất 20–30 phút với pg_basebackup — so với nhiều giờ khi dùng pg_dump. Với hệ thống production, sự khác biệt này khó có thể bỏ qua.

# Physical backup PostgreSQL
pg_basebackup -U postgres -D /backups/base -Fp -Xs -P

# Percona XtraBackup cho MySQL
xtrabackup --backup --target-dir=/backups/mysql_base
xtrabackup --prepare --target-dir=/backups/mysql_base

Một hạn chế: physical backup yêu cầu phiên bản database chính xác khi restore và không thể đọc được bằng mắt thường. Nhưng khi downtime trực tiếp gây tốn tiền, sự khác biệt về tốc độ luôn xứng đáng với độ phức tạp thêm vào.

WAL / Binary Log Streaming (Point-in-Time Recovery)

WAL streaming là nơi backup trở nên nghiêm túc cho production. Thực hiện một physical base backup, sau đó liên tục chuyển các file Write-Ahead Log (PostgreSQL) hoặc binary log (MySQL) đến một vị trí riêng biệt. Kết quả: phục hồi đến bất kỳ giây cụ thể nào — không chỉ là cửa sổ backup theo lịch gần nhất.

# postgresql.conf: bật WAL archiving
wal_level = replica
archive_mode = on
archive_command = 'cp %p /wal_archive/%f'

# recovery.conf: khôi phục về một thời điểm cụ thể
restore_command = 'cp /wal_archive/%f %p'
recovery_target_time = '2026-03-14 14:30:00'
recovery_target_action = 'promote'

Các công cụ như pgBackRest và Barman xử lý toàn bộ vòng đời WAL — nén, xác minh, lưu giữ — mà không cần scripting thủ công.

Snapshot-Based Backup

Snapshot volume cloud — AWS EBS, GCP Persistent Disk — và snapshot cấp filesystem như ZFS hoặc LVM hoàn thành trong vài giây bất kể kích thước database. Một volume 10TB snapshot nhanh như một volume 10GB. Tốc độ đó biến chúng thành lưới an toàn vững chắc ở cấp infrastructure. Sự đánh đổi: bạn restore toàn bộ volume, không phải một bảng hay transaction cụ thể. Hữu ích như một lớp trong chiến lược tổng thể, nhưng không phải là đường recovery chính.

Ưu và Nhược Điểm của Từng Phương Pháp

  • Logical dump: Dễ cài đặt, di động qua các phiên bản, hoạt động tốt cho database dưới 50GB hoặc migration xuyên phiên bản. Recovery chậm và không hỗ trợ PITR. Đừng chỉ dựa vào phương pháp này cho database production lớn.
  • Physical backup: Nhanh, nhất quán, phù hợp cho database lớn. Yêu cầu phiên bản chính khớp khi restore và không di chuyển được qua các engine khác. Phương pháp backup chính phù hợp cho production PostgreSQL hoặc MySQL.
  • WAL/binlog streaming: RPO gần bằng không, mục tiêu recovery linh hoạt. Chi phí lưu trữ và vận hành cao hơn. Thiết yếu khi mất hơn vài phút dữ liệu là không thể chấp nhận được.
  • Snapshot: Gần như tức thì với mọi kích thước database, bảo vệ infrastructure vững chắc. Không chi tiết, gắn liền với công nghệ cloud hoặc filesystem cụ thể. Tốt nhất như một lớp bổ sung bên cạnh các chiến lược database-native.

Setup Khuyến Nghị: 3-2-1 Cộng Kiểm Thử Phục Hồi

Quy tắc 3-2-1 — ba bản sao dữ liệu, hai loại phương tiện lưu trữ khác nhau, một bản sao offsite — được áp dụng như sau cho hầu hết database production:

  • Physical backup hàng ngày lưu trữ cục bộ để phục hồi nhanh từ các sự cố thông thường
  • Stream WAL hoặc binary log liên tục đến object storage (S3, GCS) để phục hồi point-in-time
  • Full backup hàng tuần sao chép đến cloud region hoặc nhà cung cấp khác để phòng thảm họa khu vực

Điều hầu hết các team bỏ qua: thực sự kiểm thử quá trình restore. Hãy chạy buổi diễn tập phục hồi hàng tháng trên môi trường riêng biệt và xác minh dữ liệu. Với hệ thống quan trọng, tự động hóa điều đó hàng tuần. Một bản backup chưa ai từng restore chỉ là lý thuyết. Chạy thử một lần và bạn sẽ biết chính xác những chỗ còn thiếu sót.

Hướng Dẫn Triển Khai

Cài Đặt pgBackRest cho PostgreSQL

pgBackRest xử lý physical backup, WAL archiving, mã hóa và xử lý song song trong một công cụ duy nhất. Sau khi cài đặt package, cấu hình như sau:

# /etc/pgbackrest.conf
[global]
repo1-path=/backups/pgbackrest
repo1-retention-full=2
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=your-strong-encryption-key

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

# Khởi tạo và chạy full backup lần đầu
pgbackrest --stanza=mydb stanza-create
pgbackrest --stanza=mydb --log-level-console=info backup --type=full

# Backup incremental hàng ngày
pgbackrest --stanza=mydb backup --type=incr

# Khôi phục về một thời điểm cụ thể
pgbackrest --stanza=mydb --delta restore \
  --target="2026-03-14 14:30:00" \
  --target-action=promote

Tự Động Hóa MySQL Backup với XtraBackup

#!/bin/bash
# /usr/local/bin/mysql_backup.sh
BACKUP_DIR="/backups/mysql/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"

xtrabackup --backup --target-dir="$BACKUP_DIR" \
  --user=backup_user --password="$MYSQL_BACKUP_PASSWORD"

xtrabackup --prepare --target-dir="$BACKUP_DIR"

# Upload lên S3 và xóa bản local sau khi sync thành công
aws s3 sync "$BACKUP_DIR" "s3://my-bucket/mysql-backups/$(date +%Y%m%d)/" \
  --delete && rm -rf "$BACKUP_DIR"

echo "$(date): Backup hoàn tất" >> /var/log/mysql_backup.log
# crontab -e
# Full backup lúc 2 giờ sáng mỗi ngày
0 2 * * * /usr/local/bin/mysql_backup.sh >> /var/log/mysql_backup.log 2>&1

Xác Minh Tính Toàn Vẹn Backup

Sự tồn tại của file backup không đồng nghĩa với việc nó có thể restore được. Với logical dump, ít nhất hãy chạy kiểm tra danh sách và restore thử nghiệm vào một database tạm:

# Kiểm tra tính toàn vẹn nhanh của file pg_dump
pg_restore --list mydb_backup.dump | head -30

# Xác minh đầy đủ: khôi phục về database test độc lập
createdb mydb_verify
pg_restore -U postgres -d mydb_verify mydb_backup.dump

# Kiểm tra nhanh số lượng row khớp với production
psql -U postgres -d mydb_verify -c "SELECT COUNT(*) FROM users;"
psql -U postgres -d mydb -c "SELECT COUNT(*) FROM users;"

# Dọn dẹp sau khi xác minh
dropdb mydb_verify

Sau khi restore, đôi khi tôi xuất một mẫu bản ghi sang CSV để chạy script kiểm tra nhanh đối chiếu với các giá trị mong đợi. Khi cần đưa dữ liệu đó vào pipeline xác thực yêu cầu JSON, tôi dùng toolcraft.app/vi/tools/data/csv-to-json — nó chuyển đổi trực tiếp trên trình duyệt, nên không có dữ liệu production nhạy cảm nào rời khỏi máy tôi. Điều đó quan trọng khi bạn đang làm việc với dữ liệu thực trong một buổi diễn tập phục hồi.

Giám Sát Phát Hiện Lỗi Âm Thầm

Lỗi âm thầm là rủi ro lớn nhất mà hầu hết các team không chủ động theo dõi. Ba tín hiệu đáng thiết lập cảnh báo:

  • Exit code của job backup — bất kỳ exit code khác 0 đều phải kích hoạt cảnh báo ngay lập tức
  • Tuổi của file backup — nếu bản backup mới nhất đã hơn 25 giờ, có gì đó đã hỏng
  • Bất thường về kích thước backup — sự giảm đột ngột thường có nghĩa là bảng trống hoặc chạy không đầy đủ
#!/bin/bash
# /usr/local/bin/check_backup_age.sh
BACKUP_FILE="/backups/mydb_latest.dump"
MAX_AGE=90000  # 25 giờ tính bằng giây

if [ ! -f "$BACKUP_FILE" ]; then
  echo "CRITICAL: Không tìm thấy file backup" | mail -s "Cảnh Báo DB Backup" [email protected]
  exit 1
fi

FILE_AGE=$(( $(date +%s) - $(stat -c %Y "$BACKUP_FILE") ))
if [ "$FILE_AGE" -gt "$MAX_AGE" ]; then
  echo "WARNING: Backup đã cũ (${FILE_AGE}s tuổi, tối đa ${MAX_AGE}s)" | \
    mail -s "Cảnh Báo DB Backup" [email protected]
fi
# Chạy kiểm tra mỗi giờ qua cron
0 * * * * /usr/local/bin/check_backup_age.sh

Recovery Mới Là Thước Đo Thực Sự

Bắt đầu với logical dump nếu bạn đang cài đặt lần đầu. Chúng dễ tha thứ, dễ suy luận và bạn có thể có thứ gì đó hoạt động trong chưa đầy một giờ. Khi database vượt quá 50GB, hãy chuyển sang physical backup với WAL hoặc binary log streaming. Thêm cloud snapshot để bảo vệ ở cấp infrastructure với mọi kích thước.

Các team xử lý sự cố tốt có một điểm chung: họ đã luyện tập. Họ biết thời gian phục hồi thực tế của mình — không phải con số ước tính trong runbook, mà là con số thật từ một buổi diễn tập thực tế. Họ tin tưởng backup của mình vì họ đã tận mắt xem quá trình restore. Hãy xây dựng sự tự tin đó trước khi bạn phải làm điều đó lúc 2 giờ sáng dưới áp lực.

Share: