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 (?, ?)