Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Đọc cách ly ảnh chụp nhanh đã cam kết

[Xem chỉ mục cho toàn bộ chuỗi]

SQL Server cung cấp hai triển khai vật lý của đã đọc cam kết mức cách ly được xác định bởi tiêu chuẩn SQL, khóa cách ly đã đọc cam kết và đọc cách ly ảnh chụp nhanh đã cam kết ( RCSI ). Mặc dù cả hai cách triển khai đều đáp ứng các yêu cầu được đặt ra trong tiêu chuẩn SQL đối với các hành vi cô lập được cam kết đọc, RCSI có các hành vi vật lý khá khác so với việc triển khai khóa mà chúng ta đã xem xét trong bài trước của loạt bài này.

Đảm bảo logic

Tiêu chuẩn SQL yêu cầu một giao dịch hoạt động ở mức cách ly được cam kết đọc không gặp phải bất kỳ lần đọc bẩn nào. Một cách khác để thể hiện yêu cầu này là nói rằng một giao dịch đã cam kết đã đọc chỉ phải gặp dữ liệu đã cam kết .

Tiêu chuẩn cũng nói rằng đọc các giao dịch đã cam kết có thể trải nghiệm các hiện tượng đồng thời được gọi là các lần đọc và ảo ảnh không lặp lại (mặc dù chúng không thực sự bắt buộc phải làm như vậy). Khi nó xảy ra, cả hai triển khai vật lý của cách ly được cam kết đọc trong SQL Server đều có thể gặp phải các lần đọc không lặp lại và các hàng ảo, mặc dù các chi tiết chính xác khá khác nhau.

Chế độ xem theo thời gian của dữ liệu đã cam kết

Nếu tùy chọn cơ sở dữ liệu READ_COMMITTED_SNAPSHOT trong ON , SQL Server sử dụng triển khai lập phiên bản hàng của mức cách ly được cam kết đã đọc. Khi điều này được bật, các giao dịch yêu cầu cách ly được cam kết đọc sẽ tự động sử dụng triển khai RCSI; không cần thay đổi mã T-SQL hiện có để sử dụng RCSI. Lưu ý cẩn thận mặc dù điều này không giống nhau khi nói rằng mã sẽ hoạt động giống nhau trong RCSI như khi sử dụng triển khai khóa của read cam kết, trên thực tế, điều này nói chung là không phải trường hợp .

Không có gì trong tiêu chuẩn SQL yêu cầu dữ liệu được đọc bởi một giao dịch được cam kết đã đọc phải là gần đây nhất dữ liệu cam kết. Việc triển khai SQL Server RCSI tận dụng lợi thế này để cung cấp các giao dịch với chế độ xem điểm trong thời gian dữ liệu đã cam kết, trong đó thời điểm đó là thời điểm tuyên bố hiện tại bắt đầu thực hiện (không phải thời điểm bất kỳ giao dịch có chứa nào bắt đầu).

Điều này hoàn toàn khác với hành vi của SQL Server khóa triển khai đã đọc cam kết, trong đó câu lệnh nhìn thấy dữ liệu được cam kết gần đây nhất kể từ thời điểm mỗi mục được đọc thực tế . Khóa đã đọc các bản phát hành đã cam kết được chia sẻ sẽ khóa càng nhanh càng tốt, do đó, tập hợp dữ liệu gặp phải có thể đến từ các thời điểm rất khác nhau.

Tóm lại, khóa đã cam kết đọc sẽ thấy từng hàng giống như tại thời điểm đó, nó đã được khóa trong thời gian ngắn và có thể đọc được về mặt vật lý; RCSI thấy tất cả các hàng như họ tại thời điểm tuyên bố bắt đầu. Cả hai cách triển khai đều được đảm bảo không bao giờ nhìn thấy dữ liệu chưa được cam kết, nhưng dữ liệu mà chúng gặp phải có thể rất khác nhau.

Ý nghĩa của chế độ xem điểm trong thời gian

Việc xem một chế độ xem tại thời điểm của dữ liệu đã cam kết có vẻ rõ ràng là vượt trội hơn so với hành vi phức tạp hơn của việc triển khai khóa. Ví dụ:rõ ràng là chế độ xem theo thời gian không thể gặp phải sự cố thiếu hàng hoặc gặp phải cùng một hàng nhiều lần , cả hai đều có thể thực hiện được khi khóa cách ly đã cam kết đã đọc.

Một ưu điểm quan trọng thứ hai của RCSI là nó không có được các khóa chia sẻ khi đọc dữ liệu, vì dữ liệu đến từ cửa hàng phiên bản hàng chứ không phải được truy cập trực tiếp. Việc thiếu các khóa dùng chung có thể cải thiện đáng kể tính đồng thời bằng cách loại bỏ xung đột với các giao dịch đồng thời nhằm tìm kiếm các khóa không tương thích. Ưu điểm này thường được tóm tắt bằng cách nói rằng người đọc không chặn người viết theo RCSI và ngược lại. Như một hệ quả khác của việc giảm chặn do các yêu cầu khóa không tương thích, cơ hội cho bế tắc thường bị giảm đáng kể khi chạy theo RCSI.

Tuy nhiên, những lợi ích này không đến nếu không có chi phí và lưu ý . Đối với một điều, việc duy trì các phiên bản của các hàng đã cam kết sẽ tiêu tốn tài nguyên hệ thống, vì vậy, điều quan trọng là môi trường vật lý phải được định cấu hình để đối phó với điều này, chủ yếu về mặt tempdb yêu cầu về hiệu suất và bộ nhớ / dung lượng ổ đĩa.

Cảnh báo thứ hai tinh tế hơn một chút:RCSI cung cấp chế độ xem ảnh chụp nhanh của dữ liệu đã cam kết như ban đầu khi bắt đầu câu lệnh, nhưng không có gì ngăn cản việc thay đổi dữ liệu thực (và những thay đổi đó được cam kết) trong khi câu lệnh RCSI đang thực thi. Không có ổ khóa dùng chung, hãy nhớ. Hệ quả tức thì của điểm thứ hai này là mã T-SQL chạy theo RCSI có thể đưa ra quyết định dựa trên thông tin lỗi thời , so 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ẽ nói thêm về vấn đề này ngay sau đây.

Có một quan sát cuối cùng (cụ thể về triển khai) mà tôi muốn thực hiện về RCSI trước khi chúng ta tiếp tục. Hàm vô hướng và đa câu lệnh thực thi bằng cách sử dụng ngữ cảnh T-SQL nội bộ khác với câu lệnh chứa. Điều này có nghĩa là chế độ xem điểm trong thời gian được nhìn thấy bên trong lệnh gọi hàm vô hướng hoặc đa câu lệnh có thể muộn hơn chế độ xem điểm trong thời gian mà phần còn lại của câu lệnh nhìn thấy. Điều này có thể dẫn đến mâu thuẫn không mong muốn, vì các phần khác nhau của cùng một câu lệnh hiển thị dữ liệu từ các thời điểm khác nhau . Hành vi kỳ lạ và khó hiểu này không áp dụng cho các hàm nội dòng, những hàm này xem ảnh chụp nhanh giống như câu lệnh mà chúng xuất hiện trong đó.

Các lần đọc và pha bóng không lặp lại

Với chế độ xem tại thời điểm cấp câu lệnh về trạng thái đã cam kết của cơ sở dữ liệu, có thể không rõ ràng ngay lập tức về cách một giao dịch đã cam kết đã đọc theo RCSI có thể gặp phải hiện tượng hàng ảo hoặc đọc không lặp lại. Thật vậy, nếu chúng ta giới hạn suy nghĩ của mình trong phạm vi của một tuyên bố duy nhất , cả hai hiện tượng này đều không thể xảy ra theo RCSI.

Đọc cùng một dữ liệu nhiều lần trong cùng một câu lệnh theo RCSI sẽ luôn trả về các giá trị dữ liệu giống nhau, không có dữ liệu nào biến mất giữa các lần đọc đó và cũng không có dữ liệu mới nào xuất hiện. Nếu bạn đang tự hỏi loại câu lệnh nào có thể đọc cùng một dữ liệu nhiều lần, hãy nghĩ về các truy vấn tham chiếu đến cùng một bảng nhiều lần, có thể là trong một truy vấn con.

Tính nhất quán đọc ở cấp độ câu lệnh là một hệ quả hiển nhiên của việc đọc được cấp dựa trên một ảnh chụp nhanh cố định của dữ liệu. Lý do mà RCSI không cung cấp sự bảo vệ khỏi các lần đọc không lặp lại và các hiện tượng ảo là các hiện tượng tiêu chuẩn SQL này được xác định ở cấp độ giao dịch. Nhiều câu lệnh trong một giao dịch đang chạy tại RCSI có thể thấy dữ liệu khác nhau, bởi vì mỗi câu lệnh nhìn thấy một chế độ xem tại thời điểm tại thời điểm câu lệnh cụ thể đó đã bắt đầu.

Tóm lại, mỗi câu lệnh trong một giao dịch RCSI nhìn thấy một tập dữ liệu được cam kết tĩnh, nhưng tập hợp đó có thể thay đổi giữa các câu lệnh bên trong cùng một giao dịch.

Dữ liệu lỗi thời

Khả năng mã T-SQL của chúng tôi đưa ra một quyết định quan trọng dựa trên thông tin lỗi thời là điều đáng lo ngại. Hãy xem xét một chút rằng ảnh chụp nhanh điểm trong thời gian được sử dụng bởi một câu lệnh chạy trong RCSI có thể cũ tùy ý .

Một câu lệnh chạy trong một khoảng thời gian đáng kể sẽ tiếp tục thấy trạng thái đã cam kết của cơ sở dữ liệu như khi câu lệnh bắt đầu. Trong khi đó, câu lệnh thiếu tất cả các thay đổi đã cam kết xảy ra trong cơ sở dữ liệu kể từ thời điểm đó.

Điều này không có nghĩa là các vấn đề liên quan đến việc truy cập dữ liệu cũ trong RCSI được giới hạn ở lâu dài nhưng vấn đề chắc chắn có thể rõ ràng hơn trong những trường hợp như vậy.

Một câu hỏi về thời gian

Về nguyên tắc, vấn đề dữ liệu lỗi thời này áp dụng cho tất cả các báo cáo RCSI, bất kể chúng có thể hoàn thành nhanh đến mức nào. Khoảng thời gian nhỏ đến mức nào, luôn có khả năng một thao tác đồng thời có thể sửa đổi tập dữ liệu mà chúng tôi đang làm việc mà chúng tôi không biết về sự thay đổi đó. Chúng ta hãy xem xét lại một trong những ví dụ đơn giản mà chúng tôi đã sử dụng trước đây khi khám phá hành vi của khóa read cam kết:

INSERT dbo.OverdueInvoices
SELECT I.InvoiceNumber
FROM dbo.Invoices AS I
WHERE I.TotalDue >
(
    SELECT SUM(P.Amount)
    FROM dbo.Payments AS P
    WHERE P.InvoiceNumber = I.InvoiceNumber
);

Khi chạy trong RCSI, câu lệnh này không thể xem bất kỳ sửa đổi cơ sở dữ liệu đã cam kết nào xảy ra sau khi câu lệnh bắt đầu thực thi. Mặc dù chúng tôi sẽ không gặp phải vấn đề về các hàng bị bỏ lỡ hoặc gặp phải nhiều lần có thể xảy ra khi triển khai khóa, nhưng một giao dịch đồng thời có thể thêm một khoản thanh toán phải để ngăn khách hàng bị gửi thư cảnh báo nghiêm khắc về khoản thanh toán quá hạn sau khi tuyên bố ở trên bắt đầu thực hiện.

Bạn có thể nghĩ đến nhiều vấn đề tiềm ẩn khác có thể xảy ra trong tình huống này hoặc trong những vấn đề khác tương tự về mặt khái niệm. Câu lệnh chạy càng lâu, chế độ xem cơ sở dữ liệu của nó càng trở nên lỗi thời và phạm vi dẫn đến các hậu quả có thể không mong muốn càng lớn.

Tất nhiên, có rất nhiều yếu tố giảm thiểu trong ví dụ cụ thể này. Hành vi này cũng có thể được coi là hoàn toàn có thể chấp nhận được. Rốt cuộc, gửi thư nhắc nhở vì thanh toán đến quá muộn vài giây là một hành động dễ bị bào chữa. Tuy nhiên, nguyên tắc vẫn còn.

Thất bại trong Quy tắc Kinh doanh và Rủi ro về Tính liêm chính

Các vấn đề nghiêm trọng hơn có thể phát sinh từ việc sử dụng thông tin lỗi thời hơn là gửi thư cảnh báo sớm vài giây. Có thể thấy một ví dụ điển hình về loại điểm yếu này với mã kích hoạt được sử dụng để thực thi một quy tắc toàn vẹn có lẽ quá phức tạp để thực thi với các ràng buộc toàn vẹn tham chiếu có khai báo. Để minh họa, hãy xem xét mã sau, mã này sử dụng trình kích hoạt để thực thi một biến thể của ràng buộc khóa ngoại, nhưng một biến thể thực thi mối quan hệ chỉ cho một số hàng trong bảng con nhất định:

ALTER DATABASE Sandpit
SET READ_COMMITTED_SNAPSHOT ON
WITH ROLLBACK IMMEDIATE;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
CREATE TABLE dbo.Parent (ParentID integer PRIMARY KEY);
GO
CREATE TABLE dbo.Child
(
    ChildID integer IDENTITY PRIMARY KEY,
    ParentID integer NOT NULL,
    CheckMe bit NOT NULL
);
GO
CREATE TRIGGER dbo.Child_AI
ON dbo.Child
AFTER INSERT
AS
BEGIN
    -- Child rows with CheckMe = true
    -- must have an associated parent row
    IF EXISTS
    (
        SELECT ins.ParentID
        FROM inserted AS ins
        WHERE ins.CheckMe = 1
        EXCEPT
        SELECT P.ParentID
        FROM dbo.Parent AS P
    )
    BEGIN
    	RAISERROR ('Integrity violation!', 16, 1);
        ROLLBACK TRANSACTION;
    END
END;
GO
-- Insert parent row #1
INSERT dbo.Parent (ParentID) VALUES (1);

Bây giờ, hãy xem xét một giao dịch đang chạy trong một phiên khác (sử dụng một cửa sổ SSMS khác cho việc này nếu bạn đang theo dõi). Thao tác này sẽ xóa hàng mẹ # 1, nhưng chưa cam kết:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
DELETE FROM dbo.Parent
WHERE ParentID = 1;

Quay lại phiên ban đầu của chúng tôi, chúng tôi cố gắng chèn một hàng con (đã chọn) tham chiếu đến hàng gốc này:

INSERT dbo.Child (ParentID, CheckMe)
VALUES (1, 1);

Mã kích hoạt thực thi, nhưng vì RCSI chỉ thấy cam kết dữ liệu tại thời điểm bắt đầu câu lệnh, nó vẫn nhìn thấy hàng cha (không phải là xóa không cam kết) và chèn thành công !

Giao dịch đã xóa hàng mẹ hiện có thể thực hiện thay đổi của nó thành công, khiến cơ sở dữ liệu ở trạng thái không nhất quán nói về logic kích hoạt của chúng tôi:

COMMIT TRANSACTION;
SELECT P.* FROM dbo.Parent AS P;
SELECT C.* FROM dbo.Child AS C;

Tất nhiên, đây là một ví dụ đơn giản hóa và một ví dụ có thể dễ dàng bị phá vỡ bằng cách sử dụng các cơ sở ràng buộc tích hợp sẵn. Các quy tắc kinh doanh phức tạp hơn nhiều và các ràng buộc giả toàn vẹn có thể được viết bên trong và bên ngoài trình kích hoạt . Khả năng xảy ra hành vi không chính xác theo RCSI phải rõ ràng.

Hành vi chặn và dữ liệu được cam kết mới nhất

Tôi đã đề cập trước đó rằng mã T-SQL không được đảm bảo hoạt động theo cùng một cách trong RCSI đọc được cam kết như khi sử dụng triển khai khóa. Ví dụ về mã trình kích hoạt trước đây là một minh họa tốt về điều đó, nhưng tôi cần nhấn mạnh rằng vấn đề chung không giới hạn ở trình kích hoạt .

RCSI thường không phải là một lựa chọn tốt cho bất kỳ mã T-SQL nào mà tính đúng đắn phụ thuộc vào việc chặn nếu tồn tại một thay đổi không cam kết đồng thời. RCSI cũng có thể không phải là lựa chọn phù hợp nếu mã phụ thuộc vào việc đọc hiện tại dữ liệu đã cam kết, chứ không phải dữ liệu được cam kết mới nhất tại thời điểm bắt đầu tuyên bố. Hai yếu tố này có liên quan với nhau, nhưng chúng không giống nhau.

Khóa được cam kết đọc theo RCSI

SQL Server cung cấp một cách để yêu cầu khóa đọc cam kết khi RCSI được bật, sử dụng gợi ý bảng READCOMMITTEDLOCK . Chúng tôi có thể sửa đổi trình kích hoạt của mình để tránh các vấn đề được hiển thị ở trên bằng cách thêm gợi ý này vào bảng cần hành vi chặn để thực hiện chính xác:

ALTER TRIGGER dbo.Child_AI
ON dbo.Child
AFTER INSERT
AS
BEGIN
    -- Child rows with CheckMe = true
    -- must have an associated parent row
    IF EXISTS
    (
        SELECT ins.ParentID
        FROM inserted AS ins
        WHERE ins.CheckMe = 1
        EXCEPT
        SELECT P.ParentID
        FROM dbo.Parent AS P WITH (READCOMMITTEDLOCK) -- NEW!!
    )
    BEGIN
        RAISERROR ('Integrity violation!', 16, 1);
        ROLLBACK TRANSACTION;
    END
END;

Với sự thay đổi này, nỗ lực chèn các khối hàng con có khả năng bị mồ côi cho đến khi giao dịch xóa được thực hiện (hoặc hủy bỏ). Nếu cam kết xóa, mã kích hoạt sẽ phát hiện vi phạm tính toàn vẹn và phát sinh lỗi dự kiến.

Xác định các truy vấn có thể không hoạt động chính xác theo RCSI là một nhiệm vụ không tầm thường có thể yêu cầu thử nghiệm rộng rãi để làm đúng (và hãy nhớ những vấn đề này khá chung chung và không giới hạn ở mã kích hoạt!) Ngoài ra, hãy thêm READCOMMITTEDLOCK gợi ý cho mọi bảng cần nó có thể là một quá trình tẻ nhạt và dễ xảy ra lỗi. Cho đến khi SQL Server cung cấp tùy chọn phạm vi rộng hơn để yêu cầu triển khai khóa khi cần thiết, chúng tôi vẫn gặp khó khăn với việc sử dụng các gợi ý bảng.

Lần tới

Bài tiếp theo trong loạt bài này tiếp tục việc kiểm tra của chúng tôi về cách ly ảnh chụp nhanh đã cam kết đã đọc, với việc xem xét hành vi đáng ngạc nhiên của các câu lệnh sửa đổi dữ liệu theo RCSI.

[Xem chỉ mục cho toàn bộ chuỗi]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Thông tin liên hệ đang phát triển có nghĩa là thay đổi cơ sở dữ liệu của bạn không?

  2. Tùy chọn cơ sở dữ liệu / Báo cáo sử dụng gói

  3. Một trường hợp sử dụng đơn giản cho các chỉ mục trên các khóa chính

  4. Cách thiết kế mô hình cơ sở dữ liệu cho hệ thống đặt chỗ rạp chiếu phim

  5. Lệnh TCL trong SQL