Đọc cam kết là yếu nhất thứ hai trong số bốn mức cách ly được xác định bởi tiêu chuẩn SQL. Tuy nhiên, đây là mức cách ly mặc định cho nhiều công cụ cơ sở dữ liệu, bao gồm cả SQL Server. Bài đăng này trong loạt bài về các mức cách ly và thuộc tính ACID của các giao dịch xem xét các đảm bảo logic và vật lý thực sự được cung cấp bởi cách ly được cam kết đọc.
Đảm bảo logic
Tiêu chuẩn SQL yêu cầu rằng một giao dịch đang chạy trong chế độ cách ly được cam kết đọc chỉ đọc cam kết dữ liệu. Nó thể hiện yêu cầu này bằng cách cấm hiện tượng đồng thời được gọi là đọc bẩn. Một lần đọc bẩn xảy ra khi một giao dịch đọc dữ liệu đã được ghi bởi một giao dịch khác, trước khi giao dịch thứ hai đó hoàn tất. Một cách khác để diễn đạt điều này là nói rằng việc đọc bẩn xảy ra khi một giao dịch đọc dữ liệu không được cam kết.
Tiêu chuẩn cũng đề cập rằng một giao dịch đang chạy ở mức cô lập được cam kết đọc có thể gặp phải hiện tượng đồng thời được gọi là lần đọc không lặp lại và bóng ma . Mặc dù nhiều sách giải thích những hiện tượng này theo nghĩa giao dịch có thể thấy các mục dữ liệu đã thay đổi hoặc mới nếu dữ liệu sau đó được đọc lại, giải thích này có thể củng cố quan niệm sai lầm rằng hiện tượng đồng thời chỉ có thể xảy ra bên trong một giao dịch rõ ràng có chứa nhiều câu lệnh. Đây không phải là như vậy. Một tuyên bố duy nhất mà không có giao dịch rõ ràng cũng dễ bị ảnh hưởng bởi hiện tượng bóng ma và đọc không lặp lại, như chúng ta sẽ thấy ngay sau đây.
Đó là khá nhiều tất cả các tiêu chuẩn phải nói về chủ đề cô lập cam kết đọc. Ngay từ cái nhìn đầu tiên, việc chỉ đọc dữ liệu được cam kết có vẻ như là một sự đảm bảo khá tốt về hành vi hợp lý, nhưng như mọi khi, ma quỷ lại chi tiết. Ngay khi bạn bắt đầu tìm kiếm lỗ hổng tiềm ẩn theo định nghĩa này, sẽ chỉ trở nên quá dễ dàng để tìm thấy các trường hợp mà các giao dịch đã cam kết đã đọc của chúng tôi có thể không tạo ra kết quả mà chúng tôi có thể mong đợi. Một lần nữa, chúng ta sẽ thảo luận chi tiết hơn về những vấn đề này trong giây lát.
Triển khai vật lý khác nhau
Có ít nhất hai điều có nghĩa là hành vi quan sát được của mức cách ly cam kết đã đọc có thể khá khác nhau trên các công cụ cơ sở dữ liệu khác nhau. Đầu tiên, yêu cầu tiêu chuẩn SQL để chỉ đọc dữ liệu đã cam kết không nhất thiết có nghĩa là dữ liệu đã cam kết được đọc bởi một giao dịch sẽ là gần đây nhất dữ liệu đã cam kết.
Công cụ cơ sở dữ liệu được phép đọc phiên bản đã cam kết của một hàng từ bất kỳ thời điểm nào trước đây , và vẫn tuân thủ định nghĩa tiêu chuẩn SQL. Một số sản phẩm cơ sở dữ liệu phổ biến thực hiện cách ly được cam kết đọc theo cách này. Kết quả truy vấn thu được trong quá trình triển khai phân lập đã cam kết đã đọc này có thể bị lỗi thời tùy ý , khi so sánh với trạng thái cam kết hiện tại của cơ sở dữ liệu. Chúng tôi sẽ đề cập đến chủ đề này vì nó áp dụng cho SQL Server trong bài đăng tiếp theo của loạt bài này.
Điều thứ hai tôi muốn thu hút sự chú ý của bạn là định nghĩa tiêu chuẩn SQL không ngăn không cho một triển khai cụ thể cung cấp thêm các biện pháp bảo vệ hiệu ứng đồng thời ngoài việc ngăn đọc bẩn . Tiêu chuẩn chỉ quy định rằng không được phép đọc bẩn, không yêu cầu các hiện tượng đồng thời khác phải được cho phép ở bất kỳ mức cô lập nhất định nào.
Để làm rõ hơn về điểm thứ hai này, một công cụ cơ sở dữ liệu tuân thủ tiêu chuẩn có thể triển khai tất cả các mức cách ly bằng cách sử dụng có thể tuần tự hóa hành vi nếu nó đã chọn. Một số công cụ cơ sở dữ liệu thương mại lớn cũng cung cấp việc triển khai cam kết đã đọc vượt ra ngoài việc ngăn chặn việc đọc bẩn (mặc dù không có gì đi xa bằng việc cung cấp Cách ly hoàn toàn trong ACID nghĩa của từ).
Ngoài ra, đối với một số sản phẩm phổ biến, hãy đọc cam kết sự cô lập là thấp nhất mức độ cô lập có sẵn; triển khai của họ về đọc không cam kết cách ly giống hệt như đã đọc cam kết. Điều này được tiêu chuẩn cho phép, nhưng những khác biệt này làm tăng thêm sự phức tạp cho nhiệm vụ di chuyển mã vốn đã khó khăn từ nền tảng này sang nền tảng khác. Khi nói về các hành vi của cấp độ cô lập, điều quan trọng là phải chỉ định cả nền tảng cụ thể.
Theo như tôi biết, SQL Server là duy nhất trong số các công cụ cơ sở dữ liệu thương mại chính trong việc cung cấp hai triển khai của mức cô lập đã cam kết đã đọc, mỗi mức có các hành vi vật lý rất khác nhau. Bài đăng này bao gồm phần đầu tiên trong số này, khóa đọc cam kết.
Đã cam kết đọc khóa máy chủ SQL
Nếu tùy chọn cơ sở dữ liệu READ_COMMITTED_SNAPSHOT
OFF
, SQL Server sử dụng khóa triển khai mức cách ly đã cam kết đã đọc, nơi các khóa dùng chung được thực hiện để ngăn một giao dịch đồng thời sửa đổi đồng thời dữ liệu, vì việc sửa đổi sẽ yêu cầu một khóa độc quyền, không tương thích với khóa chia sẻ.
Sự khác biệt chính giữa SQL Server khóa đã cam kết đọc và khóa đọc lặp lại (cũng sử dụng khóa chia sẻ khi đọc dữ liệu) là đã cam kết đọc giải phóng khóa chia sẻ càng sớm càng tốt , trong khi việc đọc lặp lại giữ các khóa này vào cuối giao dịch kèm theo.
Khi khóa đã cam kết đọc sẽ nhận được các khóa ở mức độ chi tiết của hàng, khóa được chia sẻ được thực hiện trên một hàng sẽ được giải phóng khi khóa dùng chung được thực hiện trên hàng tiếp theo . Ở mức độ chi tiết của trang, khóa trang được chia sẻ được phát hành khi hàng đầu tiên trên trang tiếp theo được đọc, v.v. Trừ khi gợi ý về mức độ chi tiết của khóa được cung cấp cùng với truy vấn, công cụ cơ sở dữ liệu sẽ quyết định mức độ chi tiết để bắt đầu. Lưu ý rằng các gợi ý về mức độ chi tiết chỉ được engine coi là gợi ý, một khóa ít chi tiết hơn yêu cầu có thể vẫn được thực hiện ban đầu. Các khóa cũng có thể tăng lên trong quá trình thực thi từ cấp độ hàng hoặc trang đến cấp độ phân vùng hoặc bảng tùy thuộc vào cấu hình hệ thống.
Điểm quan trọng ở đây là các khóa dùng chung thường chỉ được giữ trong thời gian rất ngắn trong khi câu lệnh đang thực thi. Để giải quyết một cách rõ ràng một quan niệm sai lầm phổ biến, khóa đã cam kết đọc không giữ các khóa được chia sẻ ở cuối câu lệnh.
Khóa các hành vi đã cam kết khi đọc
Các khóa chia sẻ ngắn hạn được sử dụng bởi triển khai cam kết đọc khóa SQL Server cung cấp rất ít đảm bảo thường được các nhà lập trình T-SQL mong đợi đối với một giao dịch cơ sở dữ liệu. Đặc biệt, một câu lệnh đang chạy trong khóa đã đọc cam kết cô lập:
- Có thể gặp cùng một hàng nhiều lần ;
- Có thể bỏ sót hoàn toàn một số hàng ; và
- Không không cung cấp chế độ xem theo thời điểm của dữ liệu
Danh sách đó có vẻ giống như một mô tả về các hành vi kỳ lạ mà bạn có thể kết hợp nhiều hơn với việc sử dụng NOLOCK
gợi ý, nhưng tất cả những điều này thực sự có thể và xảy ra khi sử dụng khóa cách ly đã đọc cam kết.
Ví dụ
Hãy xem xét nhiệm vụ đơn giản là đếm các hàng trong bảng, sử dụng truy vấn một câu lệnh rõ ràng. Trong điều kiện khóa cách ly đã cam kết đã đọc với mức độ chi tiết về khóa hàng, truy vấn của chúng tôi sẽ sử dụng một khóa chia sẻ trên hàng đầu tiên, đọc nó, nhả khóa chia sẻ, chuyển sang hàng tiếp theo, v.v. cho đến khi nó đi đến cuối cấu trúc. đang đọc. Vì lợi ích của ví dụ này, giả sử truy vấn của chúng ta đang đọc một cây b chỉ mục theo thứ tự khóa tăng dần (mặc dù nó cũng có thể sử dụng thứ tự giảm dần hoặc bất kỳ chiến lược nào khác).
Vì chỉ một hàng duy nhất được chia sẻ bị khóa tại bất kỳ thời điểm nhất định nào, rõ ràng là các giao dịch đồng thời có thể sửa đổi các hàng đã mở khóa trong chỉ mục mà truy vấn của chúng tôi đang duyệt qua. Nếu những sửa đổi đồng thời này thay đổi giá trị khóa chỉ mục, chúng sẽ khiến các hàng di chuyển xung quanh trong cấu trúc chỉ mục. Với khả năng đó, sơ đồ dưới đây minh họa hai tình huống có vấn đề có thể xảy ra:
Mũi tên trên cùng hiển thị một hàng mà chúng tôi đã đếm có khóa chỉ mục của nó được sửa đổi đồng thời để hàng đó di chuyển trước vị trí quét hiện tại trong chỉ mục, có nghĩa là hàng sẽ được đếm hai lần . Mũi tên thứ hai hiển thị một hàng mà quá trình quét của chúng tôi chưa gặp phải di chuyển ra sau vị trí quét, có nghĩa là hàng đó sẽ không được tính ở tất cả.
Không phải là chế độ xem theo thời gian
Phần trước đã chỉ ra cách khóa đọc cam kết có thể bỏ sót dữ liệu hoàn toàn hoặc đếm cùng một mục nhiều lần (nhiều hơn hai lần, nếu chúng ta không may mắn). Dấu gạch đầu dòng thứ ba trong danh sách các hành vi không mong muốn cho biết rằng việc khóa đã cam kết đọc cũng không cung cấp chế độ xem dữ liệu theo thời điểm.
Lý do đằng sau tuyên bố đó bây giờ có thể dễ dàng nhìn thấy. Ví dụ:truy vấn đếm của chúng tôi có thể dễ dàng đọc dữ liệu được chèn bởi các giao dịch đồng thời sau khi truy vấn của chúng tôi bắt đầu thực thi. Tương tự, dữ liệu mà truy vấn của chúng tôi nhìn thấy có thể được sửa đổi bởi hoạt động đồng thời sau khi truy vấn của chúng tôi bắt đầu và trước khi hoàn tất. Cuối cùng, dữ liệu chúng tôi đã đọc và đếm có thể bị xóa bởi một giao dịch đồng thời trước khi truy vấn của chúng tôi hoàn tất.
Rõ ràng, dữ liệu được nhìn thấy bởi một tuyên bố hoặc giao dịch đang chạy trong chế độ cách ly được cam kết đã đọc khóa tương ứng với không có trạng thái duy nhất nào của cơ sở dữ liệu tại bất kỳ thời điểm cụ thể nào . Dữ liệu mà chúng tôi gặp phải có thể từ nhiều thời điểm khác nhau, với yếu tố chung duy nhất là mỗi mục đại diện cho giá trị cam kết mới nhất của dữ liệu đó tại thời điểm nó được đọc (mặc dù nó có thể đã thay đổi hoặc biến mất kể từ đó).
Mức độ nghiêm trọng của những vấn đề này?
Tất cả điều này có vẻ giống như một trạng thái khá đơn giản nếu bạn đã quen nghĩ về các truy vấn câu lệnh đơn và các giao dịch rõ ràng của mình là thực thi một cách hợp lý ngay lập tức hoặc như đang chạy với một trạng thái tại thời điểm đã cam kết duy nhất của cơ sở dữ liệu khi sử dụng mức cách ly SQL Server mặc định. Nó chắc chắn không phù hợp với khái niệm cô lập theo nghĩa ACID.
Do điểm yếu rõ ràng của các đảm bảo được cung cấp bằng cách khóa cách ly đã đọc cam kết, bạn có thể bắt đầu tự hỏi làm thế nào bất kỳ mã T-SQL sản xuất của bạn đã từng hoạt động bình thường! Tất nhiên, chúng tôi có thể chấp nhận rằng việc sử dụng mức cách ly dưới mức có thể tuần tự hóa có nghĩa là chúng tôi từ bỏ cách ly giao dịch ACID hoàn toàn để đổi lấy các lợi ích tiềm năng khác, nhưng chúng ta có thể mong đợi những vấn đề này nghiêm trọng đến mức nào trong thực tế?
Các hàng bị thiếu và được đếm kép
Hai vấn đề đầu tiên này về cơ bản dựa vào hoạt động đồng thời thay đổi khóa trong cấu trúc chỉ mục mà chúng tôi hiện đang quét. Lưu ý rằng quét ở đây bao gồm phần quét phạm vi một phần của chỉ mục seek , cũng như quét bảng hoặc chỉ mục không hạn chế quen thuộc.
Nếu chúng ta đang (phạm vi) quét một cấu trúc chỉ mục có các khóa thường không được sửa đổi bởi bất kỳ hoạt động đồng thời nào, thì hai vấn đề đầu tiên này không phải là một vấn đề thực tế. Tuy nhiên, rất khó để chắc chắn về điều này, vì các kế hoạch truy vấn có thể thay đổi để sử dụng một phương pháp truy cập khác và chỉ mục được tìm kiếm mới có thể kết hợp các khóa dễ thay đổi.
Chúng tôi cũng phải lưu ý rằng nhiều truy vấn sản xuất chỉ thực sự cần một giá trị gần đúng hoặc câu trả lời cố gắng nhất cho một số loại câu hỏi. Thực tế là một số hàng bị thiếu hoặc được đếm hai lần có thể không quan trọng nhiều trong sơ đồ rộng hơn của mọi thứ. Trên một hệ thống có nhiều thay đổi đồng thời, thậm chí có thể khó chắc chắn rằng kết quả là không chính xác, do dữ liệu thay đổi quá thường xuyên. Trong tình huống đó, một câu trả lời gần đúng có thể đủ tốt cho mục đích của người tiêu dùng dữ liệu.
Không có chế độ xem theo thời gian
Vấn đề thứ ba (câu hỏi về cái gọi là chế độ xem dữ liệu tại thời điểm 'nhất quán') cũng đi đến cùng một loại cân nhắc. Đối với mục đích báo cáo, khi sự không nhất quán có xu hướng dẫn đến các câu hỏi khó xử từ người tiêu dùng dữ liệu, chế độ xem ảnh chụp nhanh thường được ưu tiên hơn. Trong các trường hợp khác, loại mâu thuẫn phát sinh do thiếu chế độ xem dữ liệu theo thời điểm có thể có thể chấp nhận được.
Các tình huống có vấn đề
Cũng có nhiều trường hợp các mối quan tâm được liệt kê sẽ quan trọng. Ví dụ:nếu bạn viết mã thực thi quy tắc kinh doanh trong T-SQL, bạn cần phải cẩn thận chọn một mức cách ly (hoặc thực hiện hành động phù hợp khác) để đảm bảo tính đúng đắn. Nhiều quy tắc nghiệp vụ có thể được thực thi bằng cách sử dụng các khóa hoặc ràng buộc ngoại, trong đó các công cụ cơ sở dữ liệu sẽ tự động xử lý sự phức tạp của việc lựa chọn mức cách ly cho bạn. Theo nguyên tắc chung, sử dụng tập hợp tính toàn vẹn của khai báo được tích hợp sẵn các tính năng thích hợp hơn để xây dựng các quy tắc của riêng bạn trong T-SQL.
Có một lớp truy vấn rộng khác không thực thi quy tắc kinh doanh per se , nhưng điều này có thể gây ra hậu quả đáng tiếc khi chạy ở mức cách ly đã cam kết đọc khóa mặc định. Những tình huống này không phải lúc nào cũng rõ ràng như các ví dụ thường được trích dẫn về chuyển tiền giữa các tài khoản ngân hàng hoặc đảm bảo rằng số dư trên một số tài khoản được liên kết không bao giờ giảm xuống dưới 0. Ví dụ:hãy xem xét truy vấn sau đây xác định các hóa đơn quá hạn làm đầu vào cho một số quy trình gửi các thư nhắc nhở có từ ngữ nghiêm khắc:
INSERT dbo.OverdueInvoices SELECT I.InvoiceNumber FROM dbo.Invoices AS INV WHERE INV.TotalDue > ( SELECT SUM(P.Amount) FROM dbo.Payments AS P WHERE P.InvoiceNumber = I.InvoiceNumber );
Rõ ràng là chúng tôi không muốn gửi thư cho một người đã trả góp đầy đủ hóa đơn của họ, đơn giản vì hoạt động cơ sở dữ liệu đồng thời tại thời điểm truy vấn của chúng tôi chạy có nghĩa là chúng tôi đã tính toán tổng không chính xác trong số các khoản thanh toán đã nhận. Tất nhiên, các truy vấn thực trên hệ thống sản xuất thực thường phức tạp hơn nhiều so với ví dụ đơn giản ở trên.
Để kết thúc cho ngày hôm nay, hãy xem truy vấn sau và xem liệu bạn có thể phát hiện ra bao nhiêu cơ hội có điều gì đó ngoài ý muốn xảy ra hay không, nếu một số truy vấn như vậy được chạy đồng thời ở mức cách ly đã cam kết đọc khóa (có thể trong khi các giao dịch không liên quan khác cũng đang sửa đổi bảng Trường hợp):
-- Allocate the oldest unallocated case ID to -- the current case worker, while ensuring -- the worker never has more than three -- active cases at once. UPDATE dbo.Cases SET WorkerID = @WorkerID WHERE CaseID = ( -- Find the oldest unallocated case ID SELECT TOP (1) C2.CaseID FROM dbo.Cases AS C2 WHERE C2.WorkerID IS NULL ORDER BY C2.DateCreated DESC ) AND ( SELECT COUNT_BIG(*) FROM dbo.Cases AS C3 WHERE C3.WorkerID = @WorkerID ) < 3;
Một khi bạn bắt đầu tìm kiếm tất cả các cách nhỏ nhất mà một truy vấn có thể bị sai ở cấp độ cô lập này, thì rất khó để dừng lại. Hãy ghi nhớ những lưu ý đã được lưu ý trước đây xung quanh nhu cầu thực sự về kết quả hoàn toàn riêng biệt và chính xác theo thời gian. Hoàn toàn tốt nếu có các truy vấn trả lại kết quả đủ tốt, miễn là bạn nhận thức được những đánh đổi mà bạn đang thực hiện bằng cách sử dụng cam kết đọc.
Lần tới
Phần tiếp theo của loạt bài này xem xét cách triển khai vật lý thứ hai của cách ly đã đọc cam kết có sẵn trong SQL Server, đọc cách ly ảnh chụp nhanh đã cam kết.
[Xem chỉ mục cho toàn bộ chuỗi]