Mặc dù gợi ý của Erwin có thể là đơn giản nhất cách để có được hành vi chính xác (miễn là bạn thử lại giao dịch của mình nếu bạn gặp ngoại lệ với SQLSTATE
của 40001), các ứng dụng xếp hàng theo bản chất của chúng có xu hướng hoạt động tốt hơn với việc chặn các yêu cầu để có cơ hội đến lượt chúng tại hàng đợi so với việc triển khai PostgreSQL của SERIALIZABLE
giao dịch, cho phép đồng thời cao hơn và có phần "lạc quan" hơn về khả năng xảy ra va chạm.
Truy vấn ví dụ trong câu hỏi, như nó viết tắt, trong READ COMMITTED
mặc định mức cô lập giao dịch sẽ cho phép hai (hoặc nhiều) kết nối đồng thời cả hai "yêu cầu" cùng một hàng từ hàng đợi. Điều gì sẽ xảy ra là:
- T1 bắt đầu và đi xa đến mức khóa hàng trong
UPDATE
giai đoạn. - T2 chồng lên T1 về thời gian thực thi và cố gắng cập nhật hàng đó. Nó chặn
COMMIT
đang chờ xử lý hoặcROLLBACK
của T1. - T1 cam kết, đã "xác nhận quyền sở hữu" hàng thành công.
- T2 cố gắng cập nhật hàng, nhận thấy rằng T1 đã có, tìm kiếm phiên bản mới của hàng, thấy rằng hàng đó vẫn đáp ứng tiêu chí lựa chọn (chỉ là
id
đó khớp) và cũng "xác nhận quyền sở hữu" hàng.
Nó có thể được sửa đổi để hoạt động chính xác (nếu bạn đang sử dụng phiên bản PostgreSQL cho phép FOR UPDATE
mệnh đề trong một truy vấn con). Chỉ cần thêm FOR UPDATE
vào cuối truy vấn con chọn id và điều này sẽ xảy ra:
- T1 bắt đầu và bây giờ sẽ khóa hàng trước khi chọn id.
- T2 chồng lên T1 về thời gian thực thi và các khối trong khi cố gắng chọn một id, đang chờ
COMMIT
hoặcROLLBACK
của T1. - T1 cam kết, đã "xác nhận quyền sở hữu" hàng thành công.
- Vào thời điểm T2 có thể đọc hàng để xem id, nó thấy rằng nó đã được xác nhận quyền sở hữu, vì vậy nó sẽ tìm id có sẵn tiếp theo.
Tại REPEATABLE READ
hoặc SERIALIZABLE
mức cô lập giao dịch, xung đột ghi sẽ gây ra lỗi mà bạn có thể bắt gặp và xác định là lỗi tuần tự hóa dựa trên SQLSTATE và thử lại.
Nếu bạn thường muốn các giao dịch CÓ THỂ XỬ LÝ nhưng bạn muốn tránh thử lại trong khu vực xếp hàng, bạn có thể thực hiện điều đó bằng cách sử dụng khóa cố vấn.