Dưới đây là bản tóm tắt các sai lầm thường gặp khi thay đổi schema cơ sở dữ liệu, đã được dịch và cấu trúc lại bằng tiếng Hàn:
Những sai lầm liên quan đến đồng thời
- Không lấy được lock
- Cập nhật quá nhiều hàng trong một lần
- Giữ transaction mở quá lâu sau khi đã lấy exclusive lock
Những sai lầm về tính chính xác của từng bước - vấn đề logic
- Sai lệch schema ngoài dự kiến
- Schema/mã ứng dụng không khớp nhau
- Dữ liệu ngoài dự kiến
Các sai lầm khác
- Chạm
statement_timeout - Dùng khóa chính số nguyên 4 byte cho bảng có thể tiếp tục tăng trưởng
- Bỏ qua hoạt động của
VACUUMvà rủi ro bloat
Trường hợp 1. Không khớp schema
- Chạy được ở môi trường phát triển/kiểm thử nhưng thất bại ở QA/Staging/Production
- Cần xác định nguyên nhân rồi cải thiện workflow để giải quyết
Trường hợp 2. Lạm dụng IF [NOT] EXISTS
- Đừng cố bỏ qua lỗi không khớp schema bằng
IF NOT EXISTS - Cần tìm và xử lý nguyên nhân gốc rễ của vấn đề
Trường hợp 3. Chạm statement_timeout
- Hãy kiểm thử trước mọi thay đổi với dữ liệu dung lượng lớn để phát hiện sớm
Trường hợp 4. Thay đổi quy mô lớn không giới hạn
- Nếu thay đổi quá nhiều hàng trong một transaction, sẽ ảnh hưởng đến các transaction khác
- Nếu chưa tinh chỉnh checkpointer, có thể tạo ra quá nhiều dữ liệu WAL
- Có thể làm bão hòa ghi đĩa và gây suy giảm hiệu năng tổng thể
- Có thể phát sinh vấn đề
VACUUM/bloat - Hãy chia nhỏ theo batch để xử lý và quản lý
VACUUM
Trường hợp 5. Chờ trong transaction sau khi đã lấy exclusive lock
- Nếu làm việc khác giữa
BEGIN/ALTER TABLE/COMMIT, lock sẽ bị giữ lâu - Sau khi lấy exclusive lock thì cần kết thúc transaction nhanh nhất có thể
Trường hợp 6. Transaction chứa cả DDL + DML khối lượng lớn
- Lock lấy ở bước DDL sẽ bị giữ lâu đến tận bước DML
- Hãy tách DDL và DML thành các transaction/bước migration riêng biệt
Trường hợp 7. Chặn các session khác do chờ lấy exclusive lock
- Autovacuum không yield cho DDL khi đang ở chế độ ngăn wraparound
- Ngay cả
SELECTcũng bị chặn trong lúc chờ lấy lock - Hãy đặt
lock_timeoutthấp và xây dựng logic retry
Trường hợp 8. Lưu ý khi tạo FK
- Khi tạo FK trên bảng lớn, sẽ mất thời gian do phải quét bảng được tham chiếu
- Định nghĩa FK với tùy chọn
not valid, rồivalidateở transaction riêng
Trường hợp 9. Lưu ý khi xóa FK
- Vì cần lock trên cả hai bảng nên cần logic retry với
lock_timeout
Trường hợp 10. Lưu ý khi thêm ràng buộc CHECK
- Sẽ phát sinh quét toàn bộ bảng nên dùng cách tiếp cận 2 bước tương tự FK
Trường hợp 11. Lưu ý khi thêm NOT NULL
- Trước Postgres 11, khi thêm
NOT NULLcho cột mới sẽ phát sinh quét bảng - Từ Postgres 11, có thể giải quyết bằng cách thêm cột
NOT NULL DEFAULT - Từ Postgres 12, có thể đặt
NOT NULLbằng cách thêm ràng buộcCHECK
Trường hợp 12. Lưu ý khi đổi kiểu dữ liệu của cột
- Có thể phát sinh ghi lại toàn bộ bảng
- Cần cách tiếp cận thêm cột mới rồi sao chép dữ liệu bằng trigger
Trường hợp 13. Lưu ý khi CREATE INDEX
- Trong OLTP nên dùng
CREATE INDEX CONCURRENTLY - Nếu tạo unique index thất bại thì cần dọn dẹp invalid index
Trường hợp 14. Lưu ý khi DROP INDEX
- Vì có vấn đề lấy lock nên dùng
DROP INDEX CONCURRENLTY
Trường hợp 15. Lưu ý khi đổi tên đối tượng
- Cần điều chỉnh thứ tự triển khai để tránh lệch giữa mã ứng dụng và schema DB
Trường hợp 16. Thêm cột có giá trị DEFAULT
- Trước PG 11 sẽ phát sinh ghi lại toàn bộ bảng
- Từ PG 11, việc thêm cột có giá trị
DEFAULTđã nhanh hơn
Trường hợp 17. Dọn phần dư khi CREATE INDEX CONCURRENTLY thất bại
- Nếu thất bại sẽ còn lại invalid index nên cần dọn trước khi retry
Trường hợp 18. Dùng khóa chính số nguyên 4 byte cho bảng lớn
- Nên dùng
int8. Hầu hết framework đã dùngint8sẵn.
Khuyến nghị
- Kiểm thử với kích thước dữ liệu thực tế
- Kiểm tra thời gian giữ exclusive lock
- Cải thiện tự động hóa triển khai
- Học hỏi từ người khác và chia sẻ kiến thức
Ý kiến của GN⁺
Bài viết này tổng hợp rất tốt nhiều sai lầm và điểm cần lưu ý có thể gặp phải khi thay đổi schema DB trong thực tế. Đặc biệt, các vấn đề liên quan đến exclusive lock được nhắc đến khá nhiều; với cơ sở dữ liệu dung lượng lớn, đây có thể là những vấn đề còn nghiêm trọng hơn.
Tác giả cũng giải thích khá cụ thể các lưu ý khi xử lý FK, NOT NULL, index... những thứ mà lập trình viên rất dễ bỏ sót. Việc hiểu và tận dụng các cải tiến theo từng phiên bản Postgres cũng có vẻ sẽ rất hữu ích.
Quan trọng hơn cả, tôi đồng tình với điểm rằng việc kiểm thử kỹ lưỡng với kích thước dữ liệu thực tế và cải thiện tự động hóa triển khai là chìa khóa để giảm thiểu rủi ro khi thay đổi schema. Có lẽ cũng đáng cân nhắc tận dụng các công cụ như Database Lab Engine cho việc kiểm thử và tự động hóa triển khai.
Mong rằng sẽ có thêm nhiều bài blog kỹ thuật chia sẻ những mẹo hữu ích như vậy. Càng nhiều thông tin như thế này được lan truyền, năng lực của các lập trình viên làm việc với cơ sở dữ liệu chắc chắn sẽ được nâng lên.
Chưa có bình luận nào.