Backup & Restore PostgreSQL với pg_dump
🎯 Mục tiêu: Thực hiện full backup bằng pg_dump, kiểm tra tính toàn vẹn bằng pg_restore --list, restore vào DB mới và xác nhận dữ liệu khớp.
🧰 Công cụ / nền tảng: Docker Desktop, PostgreSQL 16 client (psql, pg_dump, pg_restore).
📦 Chuẩn bị: Docker chạy. Cài PostgreSQL client tools trên WSL2: sudo apt-get install -y postgresql-client.
mkdir -p ~/db-backup-lab && cd ~/db-backup-lab
▶️ Các bước:
Bước 1 — Khởi động PostgreSQL và tạo dữ liệu mẫu
# Chạy PostgreSQL production (port 5432)
docker run -d --name pg-prod \
-e POSTGRES_DB=appdb \
-e POSTGRES_USER=appuser \
-e POSTGRES_PASSWORD=secret123 \
-p 5432:5432 \
postgres:16-alpine
# Chờ PostgreSQL sẵn sàng
sleep 3
until docker exec pg-prod pg_isready -U appuser -d appdb; do sleep 1; done
# Tạo schema và dữ liệu mẫu
docker exec -i pg-prod psql -U appuser -d appdb << 'SQL'
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer VARCHAR(100) NOT NULL,
amount NUMERIC(10,2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMPTZ DEFAULT NOW()
);
INSERT INTO orders (customer, amount, status) VALUES
('Alice Nguyen', 1250000, 'completed'),
('Bob Tran', 890000, 'completed'),
('Carol Le', 3100000, 'pending'),
('David Pham', 450000, 'cancelled'),
('Eve Hoang', 2200000, 'completed');
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price NUMERIC(10,2) NOT NULL,
stock INT DEFAULT 0
);
INSERT INTO products (name, price, stock) VALUES
('Laptop Pro 14', 28000000, 15),
('Phone Max X', 18500000, 42),
('Tablet Air', 12000000, 8);
SELECT 'Tables created, rows: ' || COUNT(*) FROM orders;
SQL
Bước 2 — Backup với pg_dump (custom format)
# Custom format (-Fc): compressed, parallel-restore capable
pg_dump \
-h localhost -p 5432 \
-U appuser \
-d appdb \
-Fc \
--verbose \
-f appdb_$(date +%Y%m%d_%H%M%S).backup
# Kiểm tra file backup
ls -lh *.backup
echo ""
# Xem nội dung backup (table of contents)
pg_restore --list appdb_*.backup | head -30
Bước 3 — Backup chỉ schema (không data)
# Schema-only backup (hữu ích cho DR planning)
pg_dump \
-h localhost -p 5432 -U appuser -d appdb \
--schema-only \
-f appdb_schema_$(date +%Y%m%d).sql
echo "Schema-only backup:"
wc -l appdb_schema_*.sql
# Plain SQL format (human-readable, dễ review)
pg_dump \
-h localhost -p 5432 -U appuser -d appdb \
-Fp --no-owner --no-privileges \
-f appdb_plain.sql
echo "Plain SQL backup size: $(du -h appdb_plain.sql | cut -f1)"
Bước 4 — Restore vào database mới và verify
# Khởi động PostgreSQL restore target (port 5433)
docker run -d --name pg-restore \
-e POSTGRES_DB=appdb_restored \
-e POSTGRES_USER=appuser \
-e POSTGRES_PASSWORD=secret123 \
-p 5433:5432 \
postgres:16-alpine
sleep 3
until docker exec pg-restore pg_isready -U appuser; do sleep 1; done
# Restore từ custom format backup
BACKUP_FILE=$(ls appdb_*.backup | head -1)
pg_restore \
-h localhost -p 5433 \
-U appuser \
-d appdb_restored \
--verbose \
--no-owner \
"$BACKUP_FILE"
echo ""
echo "=== RESTORE VERIFICATION ==="
# So sánh row counts giữa source và target
echo "--- Source (port 5432) ---"
psql -h localhost -p 5432 -U appuser -d appdb \
-c "SELECT 'orders' as table, COUNT(*) FROM orders UNION ALL SELECT 'products', COUNT(*) FROM products;"
echo "--- Restored (port 5433) ---"
psql -h localhost -p 5433 -U appuser -d appdb_restored \
-c "SELECT 'orders' as table, COUNT(*) FROM orders UNION ALL SELECT 'products', COUNT(*) FROM products;"
# Checksum verify (sum of amounts)
ORIG=$(psql -h localhost -p 5432 -U appuser -d appdb -tAc "SELECT SUM(amount)::text FROM orders;")
REST=$(psql -h localhost -p 5433 -U appuser -d appdb_restored -tAc "SELECT SUM(amount)::text FROM orders;")
[ "$ORIG" = "$REST" ] && echo "✅ CHECKSUM MATCH: $ORIG" || echo "❌ CHECKSUM MISMATCH: orig=$ORIG restored=$REST"
✅ Kết quả mong đợi: Backup file ~10–20KB (compressed). pg_restore --list hiện đủ tables/sequences. Row count và checksum khớp giữa source và restored. Output: ✅ CHECKSUM MATCH.
🧹 Cleanup: docker stop pg-prod pg-restore && docker rm pg-prod pg-restore && cd ~ && rm -rf ~/db-backup-lab