UPDLOCK có đảm bảo đồng thời không


8

Tôi có một bảng được gọi là tblOrderNumber có 1 hàng và 1 cột. Bảng này lưu trữ số thứ tự tiếp theo sẽ là gì cho trang web thương mại điện tử của tôi. Đó là VITAL TUYỆT VỜI rằng cùng một số thứ tự không được sử dụng nhiều lần. Hiện tại nhóm đang sử dụng quy trình được lưu trữ này và có vẻ như hoạt động tốt:

Câu hỏi của tôi là UPDLOCK có đảm bảo điều này không? Tôi cũng đã nghĩ rằng Khóa Đọc cũng được yêu cầu trên CHỌN (trong trường hợp không chắc là 2 đơn hàng được đặt trong một phần nghìn giây của nhau và đơn hàng đầu tiên chưa thực hiện CẬP NHẬT trước khi đơn hàng thứ hai thực hiện CHỌN, như tôi hiểu không có khóa đọc trong thủ tục này)?

DECLARE @NextOrderNumber INT

BEGIN TRANSACTION

SELECT @NextOrderNumber = NextOrderNumber
FROM  tblOrderNumber (UPDLOCK)

UPDATE tblOrderNumber
SET   NextOrderNumber = NextOrderNumber + 1

COMMIT

SELECT @NextOrderNumber

- Triển khai khách hàng (UPDLOCK HOẶC SERIALIZABLE sẽ tốt hơn ở đây vì tôi không nghĩ chúng ta cần khóa toàn bộ bảng?)

UPDATE dbo.tblOrderNumber WITH (SERIALIZABLE) 
SET @NextOrderNumber = NextOrderNumber, 
    NextOrderNumber = NextOrderNumber + 1; 
WHERE CustomerId=@CustomerId

Tôi đang sử dụng SQL Server 2014 nhưng tôi sẽ sớm thay đổi thành SQL Azure.


Điều này có vẻ hơi không trực quan đối với tôi - Tôi nghĩ rằng tôi muốn cập nhật bảng trước, để (a) tôi có thể sử dụng mệnh đề đầu ra sạch hơn (b) trạng thái mặc định của bảng là để hiển thị số thứ tự cuối cùng được gán, thay vào đó hơn cái tiếp theo Có một người tiếp theo dường như hơi quá hấp dẫn khi dựa vào việc đọc bên ngoài giao dịch của bạn và cho rằng đó là người bạn sẽ nhận được.
Aaron Bertrand

1
Điều gì xảy ra nếu bảng kết thúc với nhiều hơn một hàng hoặc không có hàng. Tôi hiểu điều đó không nên xảy ra nhưng không có gì để ngăn chặn nó. Nếu vậy CHỌN @NextOrderNumber = NextOrderNumber TỪ tblOrderNumber (UPDLOCK) sẽ không đặc biệt
paparazzo

Câu trả lời:


11

Câu trả lời ngắn

Khóa cập nhật là đủ, nhưng bạn có thể đạt được những gì bạn muốn đơn giản hơn với:

UPDATE dbo.tblOrderNumber WITH (SERIALIZABLE)
SET @NextOrderNumber = NextOrderNumber,
    NextOrderNumber = NextOrderNumber + 1
WHERE CustomerID = @CustomerID;

Các WITH (SERIALIZABLE)gợi ý không yêu cầu nghiêm ngặt nếu có một chỉ số duy nhất trên CustomerID.

Câu trả lời dài hơn

Gợi ý khóa cập nhật là đủ trong mã được cung cấp. Mỗi lần chỉ có một giao dịch có thể có được khóa cập nhật (U) trên tài nguyên và các khóa cập nhật được giữ đến cuối giao dịch. Khóa cập nhật được chuyển đổi thành khóa độc quyền (X) ngay trước khi thay đổi được thực hiện. Khóa độc quyền cũng được tổ chức đến cuối giao dịch.

Do đó, khóa cập nhật khi đọc cung cấp đảm bảo đồng thời đảm bảo bạn đang tìm kiếm. Để rõ ràng: Sau khi có được khóa cập nhật (bằng cách chọn), không có giao dịch nào khác có thể có được khóa cập nhật trên cùng một tài nguyên cho đến khi giao dịch đầu tiên được cam kết hoặc hủy bỏ.

Bạn cũng có thể chạy giao dịch (hoặc tuyên bố cập nhật duy nhất) ở SERIALIZABLEmức cô lập mà không có gợi ý. Rốt cuộc, đảm bảo bạn đang tìm kiếm là giao dịch sẽ thực hiện theo một lịch trình tuần tự hóa. Nếu bạn không thoải mái khi dựa vào kiến ​​thức về khóa nội bộ, việc chỉ định mức cô lập mong muốn và để SQL Server xử lý các chi tiết, có lẽ đơn giản hơn.

Rõ ràng bạn cũng sẽ cần mã để xử lý các lỗi hoặc các bế tắc có thể xảy ra. Tôi giả sử bạn bỏ qua điều này từ ví dụ của bạn. Một cách tiếp cận vành đai và niềng răng cũng sẽ SET XACT_ABORT ONcho thủ tục để đảm bảo gần như tất cả các lỗi có thể sẽ hủy bỏ giao dịch thay vì âm thầm tiếp tục.

Cũng có thể viết một triển khai khóa mạnh mẽ bằng tay bằng cách sử dụng các khóa ứng dụng (có sp_getapplocksp_releaseapplock), nhưng trong tất cả sự trung thực sử dụng mức cách ly tuần tự hóa tích hợp có lẽ là đơn giản nhất.

Để biết thêm thông tin, xem loạt bài viết của tôi:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.