5 điểm bởi GN⁺ 2024-04-29 | Chưa có bình luận nào. | Chia sẻ qua WhatsApp

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 VACUUM và 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ả SELECT cũng bị chặn trong lúc chờ lấy lock
  • Hãy đặt lock_timeout thấ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ồi validate ở 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 NULL cho 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 NULL bằng cách thêm ràng buộc CHECK

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ùng int8 sẵ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.

Chưa có bình luận nào.