MySQL
CHỌN ... ĐỂ CẬP NHẬT với CẬP NHẬT
Sử dụng các giao dịch với InnoDB (tự động cam kết đã tắt), SELECT ... FOR UPDATE cho phép một phiên tạm thời khóa một bản ghi (hoặc các bản ghi) cụ thể để không có phiên nào khác có thể cập nhật nó. Sau đó, trong cùng một giao dịch, phiên thực sự có thể thực hiện UPDATE trên cùng một bản ghi và cam kết hoặc quay trở lại giao dịch. Điều này sẽ cho phép bạn khóa bản ghi để không phiên nào khác có thể cập nhật nó trong khi có lẽ bạn thực hiện một số logic nghiệp vụ khác.
Điều này được thực hiện với khóa. InnoDB sử dụng các chỉ mục để khóa các bản ghi, vì vậy việc khóa một bản ghi hiện có có vẻ dễ dàng - chỉ cần khóa chỉ mục cho bản ghi đó.
CHỌN ... ĐỂ CẬP NHẬT với CHÈN
Tuy nhiên, để sử dụng SELECT ... FOR UPDATE với INSERT , làm cách nào để khóa chỉ mục cho một bản ghi chưa tồn tại? Nếu bạn đang sử dụng mức cô lập mặc định của REPEATABLE READ , InnoDB cũng sẽ sử dụng khoảng cách ổ khóa. Miễn là bạn biết id (hoặc thậm chí nhiều id) để khóa, sau đó InnoDB có thể khóa khoảng trống để không có bản ghi nào khác có thể được chèn vào khoảng trống đó cho đến khi chúng tôi hoàn thành nó.
Nếu id của bạn là cột tăng tự động, sau đó là SELECT ... FOR UPDATE với INSERT INTO sẽ có vấn đề vì bạn không biết id mới là gì cho đến khi bạn chèn nó vào. Tuy nhiên, vì bạn biết id mà bạn muốn chèn, SELECT ... FOR UPDATE với INSERT sẽ hoạt động.
CAVEAT
Ở mức cách ly mặc định, SELECT ... FOR UPDATE trên một bản ghi không tồn tại thì không chặn các giao dịch khác. Vì vậy, nếu cả hai giao dịch đều thực hiện SELECT ... FOR UPDATE trên cùng một bản ghi chỉ mục không tồn tại, cả hai đều sẽ nhận được khóa và không giao dịch nào có thể cập nhật bản ghi. Trên thực tế, nếu họ cố gắng, sẽ phát hiện ra một bế tắc.
Do đó, nếu bạn không muốn gặp bế tắc, bạn có thể làm như sau:
CHÈN VÀO ...
Bắt đầu giao dịch và thực hiện INSERT . Thực hiện logic kinh doanh của bạn và cam kết hoặc khôi phục giao dịch. Ngay sau khi bạn thực hiện INSERT trên chỉ mục bản ghi không tồn tại trong giao dịch đầu tiên, tất cả các giao dịch khác sẽ chặn nếu chúng cố gắng INSERT một bản ghi có cùng chỉ mục duy nhất. Nếu giao dịch thứ hai cố gắng chèn một bản ghi có cùng chỉ mục sau khi giao dịch đầu tiên thực hiện việc chèn, thì nó sẽ gặp lỗi "khóa trùng lặp". Xử lý phù hợp.
CHỌN ... KHÓA CHẾ ĐỘ CHIA SẺ
Nếu bạn chọn bằng LOCK IN SHARE MODE trước INSERT , nếu giao dịch trước đó đã chèn bản ghi đó nhưng chưa được cam kết, thì SELECT ... LOCK IN SHARE MODE sẽ chặn cho đến khi giao dịch trước đó hoàn tất.
Vì vậy, để giảm nguy cơ trùng lặp các lỗi khóa, đặc biệt nếu bạn giữ ổ khóa một lúc trong khi thực hiện logic nghiệp vụ trước khi thực hiện hoặc khôi phục chúng:
-
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE - Nếu không có bản ghi nào được trả lại thì
-
INSERT INTO FooBar (foo, bar) VALUES (?, ?)