Database Migration Zero Downtime: chiến lược thay đổi schema không làm sập production
Database migration zero downtime là cách thay đổi schema, dữ liệu và application code mà không cần tắt hệ thống, không làm request đang chạy lỗi hàng loạt, và vẫn có đường rollback rõ ràng. Với backend production, migration không còn là chuyện “chạy một file SQL lúc ít người dùng”; nó là một release plan cần phối hợp giữa database, application, job backfill, monitoring và feature flag.
Bài này tập trung vào PostgreSQL/MySQL trong hệ thống web/API production: khi thêm cột, đổi kiểu dữ liệu, tách bảng, backfill dữ liệu lớn, và dọn schema cũ mà vẫn giữ uptime.

Vì sao database migration dễ gây downtime?
Downtime thường không đến từ câu lệnh migration “sai cú pháp”, mà đến từ lock, rewrite bảng lớn, query plan thay đổi, application code đọc schema chưa sẵn sàng, hoặc backfill ăn hết I/O. Một ALTER TABLE đơn giản trên bảng nhỏ có thể an toàn; cùng câu lệnh đó trên bảng vài trăm triệu dòng có thể chặn ghi và kéo latency toàn hệ thống lên rất cao.
Các rủi ro thường gặp
- Lock schema/table: câu lệnh DDL giữ lock lâu hơn dự đoán.
- Deploy code và schema lệch pha: version cũ vẫn chạy trong lúc version mới đã đổi column.
- Backfill quá lớn: update hàng triệu dòng trong một transaction làm phình WAL/binlog và replication lag.
- Rollback không khả thi: đã drop cột hoặc convert dữ liệu mất thông tin.
Nguyên tắc expand/contract
Chiến lược an toàn nhất là chia migration thành nhiều bước tương thích ngược. Expand là thêm cấu trúc mới mà không phá code cũ. Sau đó application bắt đầu ghi/đọc song song hoặc chuyển dần qua cấu trúc mới. Cuối cùng contract là dọn phần cũ khi đã chắc chắn không còn traffic hoặc job nào phụ thuộc.
Ví dụ đổi tên column không downtime
-- Bước 1: expand, thêm column mới nullable
ALTER TABLE orders ADD COLUMN customer_note TEXT;
-- Bước 2: application dual-write cả note và customer_note
-- Bước 3: backfill theo batch nhỏ
UPDATE orders SET customer_note = note
WHERE customer_note IS NULL AND id BETWEEN $1 AND $2;
-- Bước 4: chuyển read path sang customer_note qua feature flag
-- Bước 5: contract sau vài release
ALTER TABLE orders DROP COLUMN note;
Điểm quan trọng: không drop hoặc rename trực tiếp trong cùng release với code mới. Hãy để cả version cũ và mới cùng chạy được trong giai đoạn rolling deploy.
Backfill dữ liệu lớn an toàn
Backfill nên là job riêng, idempotent, chia batch nhỏ và có khả năng resume. Tránh một transaction khổng lồ. Với dữ liệu lớn, mục tiêu là kiểm soát áp lực lên database chứ không phải chạy nhanh nhất có thể.

Mẫu backfill worker
batch_size = 1000
last_id = checkpoint.last_processed_id
loop do
rows = Order.where('id > ? AND customer_note IS NULL', last_id)
.order(:id).limit(batch_size)
break if rows.empty?
Order.transaction do
rows.each { |o| o.update_columns(customer_note: o.note) }
checkpoint.update!(last_processed_id: rows.last.id)
end
sleep 0.2 # throttle để giảm replication lag / I/O burst
end
Tạo index không khóa ghi
Với PostgreSQL, ưu tiên CREATE INDEX CONCURRENTLY cho bảng production lớn. Với MySQL/InnoDB, kiểm tra online DDL hỗ trợ engine/version đang dùng, ví dụ ALGORITHM=INPLACE hoặc LOCK=NONE khi phù hợp. Đừng assume staging nhỏ phản ánh lock behavior của production.
Release plan thực tế cho migration zero downtime
- Pre-check: đo số dòng, index hiện có, query đang đọc/ghi cột liên quan.
- Expand schema: thêm column/table/index theo cách tương thích ngược.
- Deploy code tương thích: version mới không làm version cũ chết.
- Dual-write hoặc shadow-read: so sánh dữ liệu mới/cũ trước khi switch.
- Backfill có throttle: theo batch, checkpoint, retry được.
- Switch read path: dùng feature flag/canary để giảm blast radius.
- Observe: latency, error rate, DB locks, replication lag, queue lag.
- Contract: chỉ drop schema cũ sau khi đã qua ít nhất một release ổn định.

Checklist trước khi chạy production
- Migration đã được thử trên bản dữ liệu gần production hoặc ít nhất có estimate thời gian/lock.
- Câu lệnh DDL không giữ lock lâu trên bảng nóng; có phương án online index.
- Application version N và N+1 cùng tương thích với schema trung gian.
- Backfill có checkpoint, retry idempotent, throttle và metric tiến độ.
- Có rollback plan: tắt feature flag, dừng job, revert read path; không phụ thuộc vào drop dữ liệu.
- Có dashboard theo dõi error rate, p95/p99 latency, DB lock wait, replication lag, CPU/I/O.
Đọc tiếp trong cluster backend production
- Backend Engineering là gì?
- Deploy Backend lên Production Checklist
- Index trong PostgreSQL và query performance
- Outbox Pattern trong Backend
Kết luận
Migration zero downtime không phải một câu lệnh thần kỳ, mà là kỷ luật release: schema mới phải tương thích ngược, dữ liệu được backfill có kiểm soát, code được chuyển dần, và phần cũ chỉ bị dọn khi đã có bằng chứng an toàn. Nếu team làm backend production, đây là năng lực bắt buộc để tránh biến một thay đổi schema nhỏ thành incident thật.