Một vấn đề mở rộng quy mô nghiêm trọng với tính năng LISTEN/NOTIFY của PostgreSQL đã gây ra cuộc thảo luận rộng rãi trong cộng đồng nhà phát triển sau khi Recall.ai chia sẻ trải nghiệm của họ về các vấn đề hiệu suất cơ sở dữ liệu. Công ty phát hiện ra rằng tính năng tưởng chừng vô hại này có thể làm sập toàn bộ hệ thống cơ sở dữ liệu dưới tải ghi đồng thời nặng.
Vấn đề khóa toàn cục ẩn giấu
Tính năng LISTEN/NOTIFY của PostgreSQL, thường được sử dụng cho thông báo thời gian thực và các mẫu pub-sub, chứa một hạn chế kiến trúc đáng ngạc nhiên. Khi một lệnh NOTIFY chạy trong một giao dịch, nó sẽ thu được một khóa toàn cục trên toàn bộ cơ sở dữ liệu trong giai đoạn commit. Điều này thực sự biến tất cả các commit cơ sở dữ liệu thành một hàng đơn, hoàn toàn phá hủy khả năng xử lý nhiều writer đồng thời.
Vấn đề trở nên đặc biệt nghiêm trọng trong các tình huống đồng thời cao. Recall.ai đã trải nghiệm điều này trực tiếp khi xử lý hàng triệu giờ ghi âm cuộc họp với hàng chục nghìn writer đồng thời. Tải cơ sở dữ liệu của họ tăng vọt đáng kể, nhưng việc sử dụng CPU và đĩa thực sự giảm mạnh - một dấu hiệu rõ ràng cho thấy hệ thống đang bị kẹt chờ khóa thay vì thực hiện công việc thực tế.
Lưu ý: Khóa toàn cục có nghĩa là chỉ một thao tác có thể tiến hành tại một thời điểm trên toàn bộ cơ sở dữ liệu, chặn tất cả các giao dịch khác hoàn thành.
Vấn đề mở rộng quy mô của PostgreSQL LISTEN/NOTIFY:
- Vấn đề: Khóa cơ sở dữ liệu toàn cục trong giai đoạn commit NOTIFY
- Tác động: Tuần tự hóa tất cả các commit cơ sở dữ liệu dưới tác vụ ghi đồng thời
- Triệu chứng: Tải cơ sở dữ liệu cao, sử dụng CPU/ổ đĩa thấp, thông lượng truy vấn giảm mạnh
- Ngưỡng quy mô: Trở thành vấn đề với hàng chục nghìn tác vụ ghi đồng thời
- Loại khóa: AccessExclusiveLock trên đối tượng 0 của lớp 1262 của cơ sở dữ liệu 0
![]() |
---|
Các chỉ số hiệu suất hệ thống làm nổi bật tác động của khóa toàn cục đối với các hoạt động cơ sở dữ liệu |
Phản ứng cộng đồng và các giải pháp thay thế
Cộng đồng nhà phát triển đã phản ứng với sự pha trộn giữa thái độ chúng tôi đã nói với bạn rồi và mối quan tâm thực sự về hạn chế được ghi chép kém này. Nhiều nhà phát triển có kinh nghiệm chỉ ra rằng PostgreSQL xuất sắc trong các thao tác dữ liệu quan hệ nhưng gặp khó khăn với các tải pub-sub đồng thời cao.
Một số phương pháp thay thế đã xuất hiện từ cuộc thảo luận. Một số nhà phát triển khuyến nghị sử dụng các hàng đợi tin nhắn chuyên dụng như NATS hoặc Redis cho các thao tác pub-sub, trong khi những người khác đề xuất các giải pháp dựa trên polling sử dụng tính năng FOR UPDATE SKIP LOCKED
của PostgreSQL. Phương pháp giám sát WAL (Write-Ahead Log) cũng nhận được sự chú ý như một cách để phát hiện các thay đổi cơ sở dữ liệu mà không gặp vấn đề khóa.
Lưu ý: Giám sát WAL liên quan đến việc theo dõi nhật ký giao dịch của PostgreSQL để tìm các thay đổi, cung cấp một cách để phản ứng với các cập nhật cơ sở dữ liệu mà không sử dụng LISTEN/NOTIFY.
Các Giải Pháp Thay Thế Được Thảo Luận:
- Phương pháp polling: Sử dụng
FOR UPDATE SKIP LOCKED
với exponential backoff - Message queues: NATS , Redis , hoặc Kafka cho các thao tác pub-sub
- Giám sát WAL: Lắng nghe PostgreSQL Write-Ahead Log để theo dõi các thay đổi
- Theo dõi ở tầng ứng dụng: Chuyển logic thông báo ra khỏi cơ sở dữ liệu
- Transactional outbox pattern: Xử lý thông báo riêng biệt sau khi commit
Bài học cho kiến trúc cơ sở dữ liệu
Sự cố này làm nổi bật một vấn đề rộng lớn hơn trong phát triển phần mềm hiện đại: sự cám dỗ sử dụng PostgreSQL như một con dao đa năng cho tất cả nhu cầu dữ liệu. Mặc dù PostgreSQL cực kỳ linh hoạt, việc đẩy nó vượt quá các trường hợp sử dụng tối ưu có thể dẫn đến các nút thắt cổ chai bất ngờ.
Cuộc thảo luận cộng đồng tiết lộ rằng nhiều nhà phát triển đã gặp phải các vấn đề tương tự với các tính năng khác của PostgreSQL như triggers và row-level security dưới tải cao. Sự đồng thuận dường như là mặc dù các tính năng này hoạt động tốt cho tải trung bình, chúng không mở rộng tuyến tính với sự gia tăng đồng thời.
Cơ sở dữ liệu là để lưu trữ dữ liệu. Dữ liệu đi vào và dữ liệu đi ra, nhưng dữ liệu không được quyết định điều gì xảy ra tiếp theo dựa trên chính nó. Đó là việc của mã ứng dụng.
Điểm mấu chốt là tầm quan trọng của việc kiểm tra tải và hiểu các tác động kiến trúc của các tính năng cơ sở dữ liệu trước khi triển khai chúng trong sản xuất. Những gì hoạt động hoàn hảo cho hàng nghìn thao tác có thể hoàn toàn thất bại ở hàng trăm nghìn.
Tiến về phía trước
Đối với các nhà phát triển hiện đang sử dụng LISTEN/NOTIFY, điều này không có nghĩa là hoảng loạn ngay lập tức. Tính năng này hoạt động tốt cho hầu hết các ứng dụng với tải trung bình. Tuy nhiên, đáng để xem xét các lựa chọn thay thế nếu bạn đang lên kế hoạch cho sự tăng trưởng đáng kể hoặc đã gặp phải các vấn đề hiệu suất.
Sự cố này cũng chứng minh giá trị của việc giám sát và ghi nhật ký thích hợp. Recall.ai có thể xác định loại khóa cụ thể gây ra vấn đề của họ chỉ sau khi bật ghi nhật ký khóa chi tiết, điều này giúp họ truy vết vấn đề về tính năng LISTEN/NOTIFY.
Trường hợp này phục vụ như một lời nhắc nhở rằng ngay cả các hệ thống cơ sở dữ liệu trưởng thành, được đánh giá cao như PostgreSQL cũng có các hạn chế mở rộng ở những nơi bất ngờ. Sự bảo vệ tốt nhất là kiểm tra kỹ lưỡng, giám sát thích hợp và có sẵn các kế hoạch di chuyển khi bạn đạt đến những giới hạn đó.
Tham khảo: Postgres LISTEN/NOTIFY does not scale