Quản lý đồng thời khi sử dụng mẫu CHỌN-CẬP NHẬT


25

Giả sử bạn có đoạn mã sau (xin vui lòng bỏ qua rằng nó quá tệ):

BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else

Trước mắt tôi, đây KHÔNG phải là quản lý đồng thời đúng cách. Chỉ vì bạn có một giao dịch không có nghĩa là người khác sẽ không đọc cùng giá trị mà bạn đã làm trước khi bạn nhận được tuyên bố cập nhật của mình.

Bây giờ, để lại mã như hiện tại (tôi nhận ra điều này được xử lý tốt hơn dưới dạng một câu lệnh hoặc thậm chí tốt hơn bằng cách sử dụng cột tự động / nhận dạng), những cách chắc chắn để làm cho nó xử lý đồng thời đúng cách và ngăn chặn các điều kiện cuộc đua cho phép hai khách hàng có được cùng một giá trị id?

Tôi khá chắc chắn rằng việc thêm một WITH (UPDLOCK, HOLDLOCK)vào CHỌN sẽ thực hiện thủ thuật. Mức cô lập giao dịch SERIALIZABLE dường như cũng hoạt động vì nó từ chối bất kỳ ai khác đọc những gì bạn đã làm cho đến khi tran kết thúc ( CẬP NHẬT : điều này là sai. Xem câu trả lời của Martin). Điều đó có đúng không? Cả hai sẽ làm việc tốt như nhau? Là một trong những ưa thích hơn khác?

Hãy tưởng tượng làm một cái gì đó hợp pháp hơn một bản cập nhật ID - một số tính toán dựa trên bài đọc mà bạn cần cập nhật. Có thể có nhiều bảng liên quan, một số bảng bạn sẽ viết và những bảng khác bạn sẽ không viết. Thực hành tốt nhất ở đây là gì?

Đã viết câu hỏi này, tôi nghĩ rằng các gợi ý khóa là tốt hơn bởi vì sau đó bạn chỉ khóa các bảng bạn cần, nhưng tôi đánh giá cao đầu vào của bất kỳ ai.

Tái bút: Không, tôi không biết câu trả lời hay nhất và thực sự muốn hiểu rõ hơn! :)


Chỉ cần làm rõ: bạn có muốn ngăn 2 khách hàng đọc cùng một giá trị hoặc phát hành updatecó thể dựa trên dữ liệu quá cũ không? Nếu sau này, bạn có thể sử dụng rowversioncột để kiểm tra xem hàng cần cập nhật đã không bị thay đổi kể từ khi nó được đọc.
a1ex07

Chúng tôi không muốn khách hàng thứ hai nhận được giá trị id cũ trước khi được khách hàng đầu tiên cập nhật thành giá trị mới. Nó nên chặn.
ErikE

Câu trả lời:


11

Chỉ cần giải quyết các SERIALIZABLEkhía cạnh mức độ cô lập. Có điều này sẽ làm việc nhưng với rủi ro bế tắc.

Hai giao dịch sẽ có thể đọc hàng đồng thời. Chúng sẽ không chặn nhau vì chúng sẽ lấy Skhóa đối tượng hoặc chỉ mụcRangeS-S phụ thuộc vào cấu trúc bảng và các khóa này tương thích . Nhưng chúng sẽ chặn nhau khi cố gắng có được các khóa cần thiết cho bản cập nhật ( IXkhóa đối tượng hoặc chỉ mục RangeS-Utương ứng) sẽ dẫn đến bế tắc.

Việc sử dụng một tường minh UPDLOCK gợi ý thay vào đó sẽ tuần tự hóa các lần đọc do đó tránh được rủi ro bế tắc.


+1 nhưng: đối với các bảng heap, bạn vẫn có thể gặp bế tắc chuyển đổi ngay cả với các khóa cập nhật: sqlblog.com/bloss/alexander_kuznetsov/archive/2009/03/11/ trên
AK

Kỳ quái, @alex. Tôi tưởng tượng nó phải làm với một điều kiện chạy đua của động cơ đang cố gắng tìm những gì để khóa trước khi thực sự TĂNG LÊN nó ...
ErikE

@ErikE - Bế tắc chuyển đổi trong bài viết của Alex đang chuyển đổi từ IXsang Xtrên chính heap. Điều thú vị là không có hàng nào đủ điều kiện nên không có khóa hàng nào được lấy ra. Không chắc chắn tại sao nó mất Xkhóa ở tất cả.
Martin Smith

11

Tôi nghĩ rằng cách tiếp cận tốt nhất cho bạn sẽ là thực sự đưa mô-đun của bạn đến mức đồng thời cao và tự mình xem. Đôi khi UPDLOCK một mình là đủ, và không cần GIỮ. Đôi khi sp_getapplock hoạt động rất tốt. Tôi sẽ không đưa ra bất kỳ tuyên bố nào ở đây - đôi khi việc thêm một chỉ mục, trình kích hoạt hoặc chế độ xem được lập chỉ mục sẽ thay đổi kết quả. Chúng ta cần nhấn mạnh mã kiểm tra và tự mình xem xét từng trường hợp.

Tôi đã viết một số ví dụ về kiểm tra căng thẳng ở đây

Chỉnh sửa: để có kiến ​​thức tốt hơn về nội bộ, bạn có thể đọc sách của Kalen Delaney. Tuy nhiên, sách có thể không đồng bộ giống như bất kỳ tài liệu nào khác. Bên cạnh đó, có quá nhiều kết hợp để xem xét: sáu cấp độ cách ly, nhiều loại khóa, chỉ mục được nhóm / không bao gồm và ai biết những gì khác. Đó là rất nhiều sự kết hợp. Trên hết, SQL Server là nguồn đóng, vì vậy chúng tôi không thể tải xuống mã nguồn, gỡ lỗi và như vậy - đó sẽ là nguồn kiến ​​thức cuối cùng. Bất cứ điều gì khác có thể không đầy đủ hoặc lỗi thời sau bản phát hành hoặc gói dịch vụ tiếp theo.

Vì vậy, bạn không nên quyết định những gì hoạt động cho hệ thống của bạn mà không có thử nghiệm căng thẳng của riêng bạn. Bất cứ điều gì bạn đã đọc, nó có thể giúp bạn hiểu những gì đang xảy ra, nhưng bạn phải chứng minh rằng những lời khuyên bạn đã đọc có hiệu quả với bạn. Tôi không nghĩ ai có thể làm điều đó cho bạn.


9

Trong trường hợp cụ thể này, việc thêm một UPDLOCKkhóa vào SELECTthực sự sẽ ngăn ngừa sự bất thường. Việc bổ sung HOLDLOCKkhông cần thiết vì khóa cập nhật được giữ trong suốt thời gian giao dịch, nhưng tôi thú nhận bao gồm bản thân nó như một thói quen (có thể xấu) trong quá khứ.

Hãy tưởng tượng làm một cái gì đó hợp pháp hơn một bản cập nhật ID, một số tính toán dựa trên bài đọc mà bạn cần cập nhật. Có thể có nhiều bảng liên quan, một số bảng bạn sẽ viết và những bảng khác bạn sẽ không viết. Thực hành tốt nhất ở đây là gì?

Không có thực hành tốt nhất. Sự lựa chọn kiểm soát tương tranh của bạn phải dựa trên yêu cầu của ứng dụng. Một số ứng dụng / giao dịch cần phải thực thi như thể chúng có quyền sở hữu độc quyền cơ sở dữ liệu, tránh sự bất thường và không chính xác bằng mọi giá. Các ứng dụng / giao dịch khác có thể chấp nhận một số mức độ can thiệp lẫn nhau.

  • Lấy mức chứng khoán có dải (<5, 10+, 50+, 100+) cho một sản phẩm trong cửa hàng web = đọc bẩn (không chính xác không thành vấn đề).
  • Kiểm tra và giảm mức cổ phiếu trên thanh toán cửa hàng web đó = đọc lặp lại (chúng ta PHẢI có cổ phiếu trước khi bán, chúng ta KHÔNG kết thúc với mức cổ phiếu âm).
  • Di chuyển tiền mặt giữa tài khoản tiết kiệm và hiện tại của tôi tại ngân hàng = tuần tự hóa (không tính toán sai hoặc lấy nhầm tiền mặt của tôi!).

Chỉnh sửa: Nhận xét của @ AlexKuznetsov đã nhắc tôi đọc lại câu hỏi và xóa lỗi rất rõ ràng trong câu trả lời của tôi. Lưu ý để tự đăng bài vào đêm khuya.

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.