Ý chính: Một thay đổi an toàn phải giả định phiên bản ứng dụng cũ và mới sẽ chạy song song trong một khoảng thời gian.
Thuật ngữ trong bài: Expand–Contract: thêm cấu trúc mới trước, chuyển dữ liệu, rồi mới bỏ cấu trúc cũ; backfill: bổ sung dữ liệu cũ vào cấu trúc mới; cutover: chuyển sang đường xử lý mới.
Bài toán thực tế
Đổi tên cột hoặc thêm ràng buộc chặt trong một lần triển khai có thể làm phiên bản cũ ngừng hoạt động. Cập nhật bảng lớn còn giữ khóa lâu và tranh tài nguyên với người dùng. Vì vậy, cần chia thay đổi thành các bước có thể quay lại.
Điểm dễ bị bỏ qua
Trong một đợt triển khai thực tế, phiên bản ứng dụng cũ và mới luôn có khoảng thời gian chạy song song. Mọi thay đổi lược đồ phải tương thích với cả hai phía trong khoảng giao nhau đó.
Expand–Contract biến một thay đổi phá vỡ thành nhiều bước nhỏ: thêm cấu trúc mới, ghi tương thích, bổ sung dữ liệu cũ, chuyển luồng đọc và chỉ xóa cấu trúc cũ khi không còn đường quay lại.
Luồng xử lý
Thêm lược đồ tương thích→Migrate
Dual read hoặc write→Xác minh
Bổ sung dữ liệu cũ và đối soát→Contract
Bỏ đường cũ
Thiết kế và triển khai
Ưu tiên expand dạng bổ sung
Thêm field hoặc table mới trước. Rename được triển khai thành add, copy, chuyển đổi chính thức rồi remove.
Bổ sung dữ liệu cũ phải resume được
Dùng khoảng primary khóa ổn định, commit nhỏ và mốc lưu an toàn. Chạy lại batch không được gây hại.
Kiểm tra constraint riêng
Tạo constraint theo cách ít blocking, sau đó kiểm tra dữ liệu cũ dưới tải được kiểm soát.
Đi sâu vào thiết kế
Rollback là bài toán dữ liệu
Code rollback nhanh nhưng dữ liệu chỉ có dạng mới thì không. Giữ reverse-compatible path cho tới khi rollback khoảng thời gian đóng.
Constraint tạo sự tin cậy
Sau đối soát, cơ sở dữ liệu constraint biến giả định thành chế độ bắt buộc. Thêm khi dữ liệu đã sạch, không phải trong lúc chuyển đổi chính thức khẩn cấp.
Cách triển khai
- Thêm cột cho phép rỗng hoặc bảng mới mà chưa thay đổi hành vi hiện tại.
- Triển khai mã tương thích, bổ sung dữ liệu cũ theo các lô có thể tiếp tục và đối soát kết quả.
- Chuyển luồng đọc bằng Feature Flag; chỉ xóa mã và lược đồ cũ trong một bản phát hành sau khi đường quay lui không còn cần thiết.
Mã minh họa: Bổ sung dữ liệu theo khóa chính và có thể tiếp tục
lastId = checkpoint.load('account_region_v2')
rows = SELECT id FROM accounts WHERE id > :lastId ORDER BY id LIMIT 1000
UPDATE accounts SET region_v2 = derive(region) WHERE id IN (:rows)
checkpoint.save(MAX(rows.id))
sleep(rateLimit)Sau mỗi lô, đo thời gian chờ khóa và độ trễ replica; tự tạm dừng trước khi độ trễ của người dùng bị ảnh hưởng.
Rủi ro cần tính trước
- Worker phiên bản cũ chạy lại và chỉ ghi vào cột cũ sau khi hệ thống đã chuyển luồng.
- Tác vụ bổ sung dữ liệu cũ ghi đè một thay đổi mới vừa được người dùng thực hiện.
- Xóa cột cũ làm hỏng câu lệnh đã chuẩn bị hoặc một báo cáo ít dùng bị bỏ quên.
Nên theo dõi gì?
| Tín hiệu | Điều tín hiệu cho biết |
|---|---|
| Số lượt đọc và ghi trên đường cũ lẫn đường mới | Cho biết đường tương thích nào vẫn còn được sử dụng. |
| Tiến độ bổ sung dữ liệu và số bản ghi sai lệch | Đo độ đầy đủ cùng tính đúng của quá trình chuyển đổi. |
| Thời gian chờ khóa và độ trễ replica | Bảo vệ lưu lượng khách hàng trong khi chuyển đổi dữ liệu. |
Cách kiểm chứng
- Chạy đồng thời application phiên bản cũ và mới xuyên suốt đợt chuyển đổi.
- Tạm dừng rồi restart bổ sung dữ liệu cũ tại mốc lưu an toàn ngẫu nhiên.
- Tạo online cập nhật đồng thời và chứng minh bổ sung dữ liệu cũ không ghi đè.
Đưa vào production từng bước
Tách từng bước expand, ứng dụng tương thích, bổ sung dữ liệu, chuyển luồng đọc và contract thành các bản phát hành độc lập. Chuyển một nhóm khách hàng nhỏ trước, giữ đường quay lui đủ lâu và chỉ xóa cấu trúc cũ sau khi số liệu cùng nhật ký kiểm toán đều xác nhận an toàn.
Checklist trước khi vận hành
- Hành vi khóa của DDL được thử trên dung lượng gần production.
- Bổ sung dữ liệu cũ có rate giới hạn, mốc lưu an toàn và nút tạm dừng.
- Chỉ contract khi dữ liệu giám sát cho thấy đường cũ không còn usage.
Điều cần nhớ
Chuyển đổi cơ sở dữ liệu không gián đoạn là một chuỗi thay đổi tương thích và có thể đảo ngược, không phải một câu SQL khéo léo.
