1. 06:56 06/08/2026
Trong quá trình phát triển web/app, việc thay đổi database là chuyện gần như chắc chắn xảy ra: thêm cột, đổi kiểu dữ liệu, tách bảng, thêm index, chuẩn hóa lại quan hệ… Nếu làm tùy hứng, một thay đổi nhỏ ở database có thể khiến ứng dụng lỗi hàng loạt, deploy thất bại hoặc mất dữ liệu khó khôi phục. Vì vậy, database migration nên được xem là một phần quan trọng của quy trình phát triển, không chỉ là việc “chạy vài câu SQL”.
Database migration là gì?
Hiểu đơn giản, migration là cách quản lý thay đổi cấu trúc database theo từng phiên bản. Thay vì sửa trực tiếp trên database production rồi ghi nhớ bằng tay, đội phát triển tạo các file migration có thứ tự rõ ràng, ví dụ:
- Thêm cột phone_number vào bảng users
- Tạo bảng user_sessions
- Thêm index cho cột order_id
- Đổi tên cột status thành payment_status
Mỗi migration nên mô tả một thay đổi cụ thể, có thể chạy tự động trên các môi trường như local, staging và production.
Vì sao không nên sửa database thủ công?
Sửa thủ công có thể nhanh trong lúc gấp, nhưng thường để lại nhiều rủi ro:
- Môi trường local, staging và production bị lệch schema
- Thành viên mới không dựng được database đúng trạng thái hiện tại
- Không biết thay đổi nào đã được áp dụng, thay đổi nào chưa
- Khó rollback khi deploy lỗi
- Dễ quên cập nhật code tương ứng với thay đổi database
Ví dụ, backend đã deploy code đọc cột users.avatar_url, nhưng production chưa được thêm cột này. Kết quả là API lỗi ngay khi có request liên quan đến user profile. Nếu có migration trong pipeline deploy, lỗi kiểu này có thể được phát hiện và kiểm soát tốt hơn.
Một quy trình migration thực tế nên như thế nào?
1. Mỗi thay đổi database đi kèm một file migration
Khi làm một tính năng cần sửa database, đừng chỉ sửa model hoặc entity trong code. Hãy tạo migration tương ứng. File migration nên có tên dễ hiểu, ví dụ:
- add_avatar_url_to_users
- create_notifications_table
- add_index_to_orders_created_at
Tên rõ ràng giúp người đọc hiểu lịch sử thay đổi mà không phải mở từng file.
2. Chạy migration ở local trước khi commit
Trước khi đưa code lên, lập trình viên nên chạy migration trên máy local và kiểm tra ứng dụng hoạt động đúng. Nếu dự án có test tự động, nên chạy test sau migration để phát hiện lỗi sớm.
Một lỗi phổ biến là tạo migration nhưng chưa kiểm tra với database trống. Thành viên mới clone project, chạy setup từ đầu thì migration bị lỗi do phụ thuộc ngầm vào dữ liệu hoặc thứ tự không đúng.
3. Kiểm tra trên staging trước production
Staging nên càng giống production càng tốt về cấu trúc. Trước khi deploy thật, hãy chạy migration trên staging để kiểm tra:
- Migration có chạy thành công không
- API cũ và mới có tương thích không
- Query có bị chậm bất thường không
- Giao diện có hiển thị đúng dữ liệu không
Nếu migration liên quan đến dữ liệu lớn, staging cũng là nơi tốt để ước lượng thời gian chạy và phát hiện câu lệnh nguy hiểm.
Các nguyên tắc giúp migration an toàn hơn
Không đổi quá nhiều thứ trong một migration
Một migration vừa thêm bảng, đổi cột, xóa dữ liệu, tạo index và cập nhật dữ liệu hàng loạt sẽ rất khó debug nếu có lỗi. Nên tách thành nhiều bước nhỏ. Khi có sự cố, bạn sẽ biết chính xác bước nào gây vấn đề.
Tránh xóa cột ngay trong cùng một lần deploy
Giả sử bạn muốn đổi cột full_name thành display_name. Cách nguy hiểm là đổi tên cột ngay, rồi deploy code mới. Nếu trong lúc deploy vẫn còn server chạy code cũ, code đó sẽ tìm full_name và bị lỗi.
Cách an toàn hơn thường là:
- Bước 1: Thêm cột display_name, vẫn giữ full_name
- Bước 2: Cập nhật code để ghi cả hai cột hoặc đọc ưu tiên cột mới
- Bước 3: Chuyển dữ liệu cũ sang cột mới
- Bước 4: Sau khi chắc chắn không còn code dùng full_name, mới xóa cột cũ
Cách này có vẻ dài hơn, nhưng giảm rủi ro khi deploy rolling, deploy nhiều instance hoặc cần rollback.
Cẩn thận với migration dữ liệu lớn
Các câu lệnh như cập nhật toàn bộ bảng lớn, thêm index trên bảng nhiều bản ghi, đổi kiểu dữ liệu của cột đang được truy cập nhiều… có thể làm database bị khóa hoặc chậm. Với dữ liệu lớn, nên cân nhắc:
- Chạy cập nhật theo batch nhỏ
- Tạo index theo cách ít khóa bảng nếu hệ quản trị database hỗ trợ
- Chạy vào thời điểm ít người dùng
- Theo dõi log, CPU, I/O và thời gian query
- Có kế hoạch dừng hoặc tiếp tục nếu migration bị gián đoạn
Ví dụ, thay vì update một triệu bản ghi trong một câu lệnh duy nhất, có thể chia theo khoảng id: mỗi lần cập nhật 5.000 hoặc 10.000 bản ghi, nghỉ ngắn giữa các batch để giảm áp lực lên database.
Luôn nghĩ đến rollback, nhưng đừng ảo tưởng rollback lúc nào cũng dễ
Nhiều framework hỗ trợ viết migration up và down. Tuy nhiên, rollback database không đơn giản như rollback code. Nếu migration đã xóa cột hoặc xóa dữ liệu, rollback không thể tự khôi phục dữ liệu đã mất nếu không có backup.
Vì vậy, với migration nguy hiểm, cần có phương án rõ ràng:
- Backup trước khi chạy
- Không xóa dữ liệu ngay nếu chưa cần
- Dùng chiến lược mở rộng trước, dọn dẹp sau
- Ghi chú cách kiểm tra sau migration
- Có người chịu trách nhiệm theo dõi trong quá trình deploy
Đừng quên index khi thay đổi truy vấn
Nhiều tính năng mới làm thay đổi cách truy vấn dữ liệu. Ví dụ, trước đây danh sách đơn hàng chỉ lọc theo user_id, nay thêm lọc theo created_at và status. Nếu không thêm index phù hợp, API có thể vẫn chạy tốt ở local nhưng chậm trên production.
Tuy nhiên, cũng không nên thêm index bừa bãi. Index giúp đọc nhanh hơn nhưng làm ghi dữ liệu tốn thêm chi phí và tăng dung lượng lưu trữ. Hãy xem xét những truy vấn quan trọng, tần suất cao, dữ liệu lớn trước khi quyết định.
Một checklist ngắn trước khi chạy migration production
- Migration đã chạy thành công ở local và staging chưa?
- Code mới có tương thích với schema cũ trong thời gian deploy không?
- Có câu lệnh nào có thể khóa bảng lâu không?
- Có cần backup trước khi chạy không?
- Có kế hoạch rollback hoặc khắc phục nếu lỗi không?
- Có ai theo dõi log và metric sau deploy không?
- Migration có xóa dữ liệu không? Nếu có, đã xác nhận thật sự an toàn chưa?
Kết luận
Database migration không chỉ là việc kỹ thuật phụ trợ, mà ảnh hưởng trực tiếp đến độ ổn định của web/app. Một migration tốt nên nhỏ, rõ ràng, có thể kiểm tra, có kế hoạch triển khai và không làm hệ thống phụ thuộc vào may mắn. Khi dự án còn nhỏ, quy trình này có vẻ hơi mất công; nhưng càng về sau, nó chính là thứ giúp đội phát triển deploy tự tin hơn, giảm lỗi production và tránh những lần “sửa nóng” đầy rủi ro.