Tại sao CTE mở để cập nhật bị mất?


8

Tôi không hiểu ý của Craig Ringer khi anh ấy bình luận:

Giải pháp này có thể bị mất cập nhật nếu giao dịch chèn quay trở lại; không có kiểm tra để thực thi rằng CẬP NHẬT ảnh hưởng đến bất kỳ hàng nào.

trên https://stackoverflow.com/a/8702291/14731 . Vui lòng cung cấp một chuỗi các sự kiện mẫu (ví dụ Chủ đề 1 làm X, Chủ đề 2 thực hiện Y) để chứng minh cách cập nhật bị mất có thể xảy ra.


1
Hỏi tôi vài điều về một bình luận tôi đã để lại hơn một năm trước về một chủ đề phức tạp ... vui! Bây giờ tôi phải nhớ vấn đề chính xác là gì. Kiểm tra lại nó bây giờ.
Craig Ringer

Câu trả lời:


14

Tôi nghĩ có lẽ tôi muốn thêm nhận xét đó vào câu trả lời trước, về hai câu riêng biệt. Nó đã hơn một năm trước, vì vậy tôi không hoàn toàn chắc chắn nữa.

Truy vấn dựa trên wCTE không thực sự giải quyết được vấn đề mà nó phải giải quyết, nhưng sau khi xem xét lại một năm sau, tôi không thấy khả năng bị mất các bản cập nhật trong phiên bản wCTE.

. bạn cần sử dụng một điểm lưu trữ giữa mỗi thay đổi.)

Phiên bản hai tuyên bố bị mất cập nhật.

Phiên bản sử dụng hai câu lệnh riêng biệt có thể bị mất các bản cập nhật trừ khi ứng dụng kiểm tra số lượng hàng bị ảnh hưởng từ UPDATEcâu lệnh và INSERTcâu lệnh và thử lại nếu cả hai đều bằng không.

Hãy tưởng tượng những gì xảy ra nếu bạn có hai giao dịch READ COMMITTEDriêng lẻ.

  • TX1 chạy UPDATE(không có hiệu lực)
  • TX1 chạy INSERT(chèn một hàng)
  • TX2 chạy UPDATE(không có hiệu lực, hàng được chèn bởi TX1 chưa hiển thị)
  • TX1 COMMITs.
  • TX2 chạy INSERT, * có ảnh chụp nhanh mới có thể thấy hàng được TX1 cam kết. Các EXISTSlợi nhuận khoản đúng, bởi vì bây giờ TX2 có thể nhìn thấy dòng chèn vào bởi TX1.

Vì vậy TX2 không có hiệu lực. Trừ khi ứng dụng kiểm tra số lượng hàng từ bản cập nhật và chèn và thử lại nếu cả hai đều báo cáo các hàng bằng 0, nó sẽ không biết rằng giao dịch không có hiệu lực và sẽ tiếp tục thực hiện.

Cách duy nhất để nó có thể kiểm tra số lượng hàng bị ảnh hưởng là chạy nó dưới dạng hai câu lệnh riêng biệt thay vì một câu lệnh đa hoặc sử dụng một thủ tục.

Bạn có thể sử dụng SERIALIZABLEcách ly, nhưng bạn vẫn sẽ cần một vòng lặp thử lại để xử lý các lỗi nối tiếp.

Phiên bản wCTE bảo vệ chống lại sự cố cập nhật bị mất vì INSERTđiều kiện là liệu điều đó có UPDATEảnh hưởng đến bất kỳ hàng nào không, thay vì trên một truy vấn riêng biệt.

WCTE không loại bỏ các vi phạm duy nhất

Phiên bản CTE có thể ghi vẫn không phải là một phiên bản đáng tin cậy.

Hãy xem xét hai giao dịch chạy đồng thời.

  • Cả hai đều thực hiện mệnh đề GIÁ TRỊ.

  • Bây giờ cả hai thực hiện UPDATEphần. Vì không có hàng nào khớp với UPDATEmệnh đề s where, cả hai đều trả về một tập kết quả trống từ bản cập nhật và không thay đổi.

  • Bây giờ cả hai chạy INSERTphần. Vì các UPDATEhàng 0 được trả về cho cả hai truy vấn, cả hai đều cố gắng vào INSERThàng.

Một người thành công. Một người ném một vi phạm độc đáo và hủy bỏ.

Điều này không gây lo ngại về việc mất dữ liệu miễn là ứng dụng kiểm tra kết quả lỗi từ các truy vấn của nó (tức là bất kỳ ứng dụng được viết nào) và thử lại, nhưng nó làm cho giải pháp không tốt hơn các phiên bản hai câu lệnh hiện có. Nó không loại bỏ sự cần thiết của một vòng lặp thử lại.

Ưu điểm mà wCTE cung cấp so với phiên bản hai câu lệnh hiện tại là nó sử dụng đầu ra của UPDATEquyết định để INSERTthay thế, thay vì sử dụng một truy vấn riêng đối với bảng. Đó là một phần tối ưu hóa, nhưng nó một phần bảo vệ chống lại sự cố với phiên bản hai câu lệnh gây ra các bản cập nhật bị mất; xem bên dưới.

Bạn có thể chạy wCTE một SERIALIZABLEcách cô lập, nhưng sau đó bạn sẽ chỉ nhận được các lỗi nối tiếp thay vì các vi phạm duy nhất. Nó sẽ không thay đổi sự cần thiết của một vòng lặp thử lại.

WCTE dường như không dễ bị cập nhật

Nhận xét của tôi cho thấy giải pháp này có thể dẫn đến mất cập nhật, nhưng khi xem xét tôi nghĩ rằng tôi có thể đã nhầm.

Cách đây hơn một năm và tôi không thể nhớ lại các trường hợp chính xác, nhưng tôi nghĩ có lẽ tôi đã bỏ lỡ thực tế là các chỉ mục duy nhất có ngoại lệ một phần từ quy tắc hiển thị giao dịch để cho phép một giao dịch chèn chờ một giao dịch khác chèn hoặc cuộn trở lại trước khi tiến hành.

Hoặc có lẽ tôi đã bỏ lỡ một thực tế rằng INSERTtrong wCTE là có điều kiện về việc liệu các UPDATEhàng có bị ảnh hưởng hay không, không phải là liệu hàng ứng cử viên có tồn tại trong bảng hay không.

Xung đột INSERTs trên một chỉ mục duy nhất chờ cam kết / rollback

Nói rằng một bản sao của truy vấn chạy, chèn một hàng. Sự thay đổi chưa được cam kết. Bộ dữ liệu mới tồn tại trong heap và chỉ mục duy nhất, nhưng nó chưa hiển thị cho các giao dịch khác, bất kể mức độ cô lập.

Bây giờ một bản sao khác của truy vấn chạy. Hàng được chèn chưa hiển thị vì bản sao đầu tiên chưa được cam kết, vì vậy bản cập nhật không khớp với bất cứ thứ gì. Truy vấn sẽ tiếp tục thử chèn, sẽ thấy rằng một giao dịch đang thực hiện khác đang chèn cùng khóa đó và sẽ chặn chờ giao dịch đó được cam kết hoặc khôi phục .

Nếu giao dịch đầu tiên được thực hiện, giao dịch thứ hai sẽ thất bại với một vi phạm duy nhất, theo các điều trên. Nếu giao dịch đầu tiên quay trở lại thì giao dịch thứ hai sẽ tiến hành thay thế.

Việc INSERTphụ thuộc vào số lượng hàng UPDATEbảo vệ chống lại các cập nhật bị mất

Không giống như trong trường hợp hai tuyên bố, tôi không nghĩ rằng wCTE dễ bị mất các bản cập nhật.

Nếu UPDATEkhông có hiệu lực, ý INSERTchí sẽ luôn luôn chạy, bởi vì nó hoàn toàn có điều kiện về việc liệu UPDATEđã làm bất cứ điều gì, không phải ở trạng thái bảng bên ngoài. Vì vậy, nó vẫn có thể thất bại với một vi phạm duy nhất, nhưng nó không thể âm thầm không có bất kỳ ảnh hưởng nào và mất hoàn toàn bản cập nhật.

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.