Deadlock trong PostgreSQL xảy ra khi hai hoặc nhiều giao dịch chờ nhau để giải phóng tài nguyên mà giao dịch kia đang giữ, dẫn đến tình trạng cả hai đều không thể tiếp tục. Khi gặp deadlock, PostgreSQL sẽ tự động phát hiện và buộc một giao dịch phải hủy bỏ, cho phép giao dịch còn lại tiếp tục.
Dưới đây là cách xử lý và phòng tránh deadlock trong PostgreSQL:
PostgreSQL tự động phát hiện deadlock khi nó xảy ra. Nếu phát hiện deadlock, một trong các giao dịch sẽ bị buộc hủy bỏ, và bạn sẽ nhận được thông báo lỗi:
ERROR: deadlock detected DETAIL: Process 1234 waits for ShareLock on transaction 5678; blocked by process 5678. Process 5678 waits for ExclusiveLock on transaction 1234; blocked by process 1234.
Trong trường hợp này, một giao dịch đã bị hủy bỏ để giải quyết deadlock.
Khi một giao dịch bị hủy do deadlock, hệ thống sẽ trả về lỗi, và giao dịch sẽ được rollback. Dưới đây là cách bạn có thể xử lý:
import psycopg2 from psycopg2 import OperationalError def execute_transaction_with_retry(conn, transaction_function, max_retries=3): retry_count = 0 while retry_count < max_retries: try: transaction_function(conn) conn.commit() break except OperationalError as e: if "deadlock detected" in str(e): print(f"Deadlock detected, retrying transaction {retry_count + 1}/{max_retries}") conn.rollback() # Rollback giao dịch để thử lại retry_count += 1 else: raise e if retry_count == max_retries: raise Exception("Transaction failed after maximum retries due to deadlock.")
Phòng tránh deadlock là phương pháp tốt nhất để giảm thiểu sự xuất hiện của chúng. Dưới đây là một số cách để phòng tránh:
Deadlock thường xảy ra khi các giao dịch khóa tài nguyên theo thứ tự khác nhau. Một phương pháp tốt để tránh deadlock là đảm bảo tất cả các giao dịch khóa tài nguyên theo một thứ tự nhất quán.
Ví dụ: Giả sử bạn có hai bảng account
và order
, và hai giao dịch khác nhau cùng truy cập và sửa đổi dữ liệu của hai bảng này. Nếu giao dịch A khóa bảng account
trước rồi đến bảng order
, thì tất cả các giao dịch khác cũng nên làm điều tương tự.
Chỉ khóa các hàng cần thiết thay vì khóa toàn bộ bảng. Sử dụng các câu lệnh SELECT ... FOR UPDATE
hoặc SELECT ... FOR SHARE
để khóa chỉ những hàng cụ thể mà bạn cần trong giao dịch thay vì khóa nhiều hàng hoặc cả bảng.
Ví dụ:
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
Hãy giữ các giao dịch ngắn và đơn giản nhất có thể. Điều này giúp giảm thời gian giữ các khóa, hạn chế khả năng xảy ra deadlock.
Trong một số trường hợp, mức độ cô lập giao dịch có thể ảnh hưởng đến khả năng xảy ra deadlock. Mức độ cô lập càng cao thì các giao dịch càng khó tương tác với nhau, dẫn đến khả năng xảy ra deadlock cao hơn.
Mức độ cô lập mặc định READ COMMITTED
có xu hướng tránh deadlock tốt hơn so với SERIALIZABLE
, mặc dù điều này phụ thuộc vào tình huống cụ thể.
Nếu một giao dịch quá lớn hoặc quá phức tạp, bạn có thể chia nhỏ nó thành các giao dịch nhỏ hơn để giảm thời gian khóa tài nguyên.
PostgreSQL cung cấp công cụ ghi lại thông tin về deadlock trong log hệ thống. Bạn có thể bật tùy chọn deadlock_timeout
trong cấu hình PostgreSQL để xác định khoảng thời gian mà hệ thống sẽ chờ trước khi kiểm tra deadlock.
deadlock_timeout
trong postgresql.conf
:deadlock_timeout = '1s' # 1 giây là thời gian chờ trước khi phát hiện deadlock
Để xử lý deadlock trong PostgreSQL, bạn có thể: