Tôi đã nghe nói về các vấn đề tương tranh như thế trong MySQL trước đây. Không như vậy trong Postgres.
Khóa cấp độ tích hợp trong READ COMMITTED
mức cô lập giao dịch mặc định là đủ.
Tôi đề xuất một câu lệnh với CTE sửa đổi dữ liệu (thứ mà MySQL cũng không có) bởi vì nó thuận tiện để chuyển trực tiếp các giá trị từ bảng này sang bảng khác (nếu bạn cần điều đó). Nếu bạn không cần bất cứ điều gì từ các coupon
bảng mà bạn có thể sử dụng một giao dịch với riêng UPDATE
và INSERT
báo cáo chỉ là tốt.
WITH upd AS (
UPDATE coupon
SET used = true
WHERE coupon_id = 123
AND NOT used
RETURNING coupon_id, other_column
)
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;
Nó là một điều hiếm hoi mà nhiều hơn một giao dịch cố gắng mua lại cùng một phiếu giảm giá. Họ có một số duy nhất, phải không? Nhiều hơn một giao dịch cố gắng tại cùng một thời điểm sẽ hiếm hơn nhiều. (Có thể là một lỗi ứng dụng hoặc ai đó đang cố gắng chơi trò chơi hệ thống?)
Là như nó có thể, UPDATE
chỉ thành công cho chính xác một giao dịch, không có vấn đề gì. Một UPDATE
có được một khóa cấp hàng trên mỗi hàng mục tiêu trước khi cập nhật. Nếu một giao dịch đồng thời cố gắng vào UPDATE
cùng một hàng, nó sẽ thấy khóa trên hàng đó và đợi cho đến khi giao dịch chặn kết thúc ( ROLLBACK
hoặc COMMIT
), sau đó trở thành đầu tiên trong hàng đợi khóa:
Nếu cam kết, kiểm tra lại điều kiện. Nếu vẫn còn NOT used
, khóa hàng và tiến hành. Khác UPDATE
bây giờ không tìm thấy hàng đủ điều kiện và không làm gì , không trả lại hàng, vì vậy INSERT
cũng không có gì.
Nếu cuộn lại, khóa hàng và tiến hành.
Không có tiềm năng cho một điều kiện cuộc đua .
Không có khả năng xảy ra bế tắc trừ khi bạn đặt nhiều ghi vào cùng một giao dịch hoặc nếu không thì khóa nhiều hàng hơn chỉ một hàng.
Việc INSERT
này là vô tư. Nếu, do một số sai lầm coupon_id
đã có trong log
bảng (và bạn có một ràng buộc ĐỘC ĐÁO hoặc PK log.coupon_id
), toàn bộ giao dịch sẽ được khôi phục sau một vi phạm duy nhất. Sẽ chỉ ra một trạng thái bất hợp pháp trong DB của bạn. Nếu tuyên bố trên là cách duy nhất để ghi vào log
bảng, điều đó sẽ không bao giờ xảy ra.