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

Mức cô lập SNAPSHOT

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

Vấn đề đồng thời cũng khó giống như cách mà lập trình đa luồng cũng khó. Trừ khi sử dụng cách ly có thể tuần tự hóa, có thể khó mã hóa các giao dịch T-SQL luôn hoạt động chính xác khi người dùng khác thực hiện thay đổi cơ sở dữ liệu cùng một lúc.

Các vấn đề tiềm ẩn có thể không nhỏ ngay cả khi 'giao dịch' được đề cập là một SELECT đơn giản tuyên bố. Đối với các giao dịch nhiều câu lệnh phức tạp đọc và ghi dữ liệu, khả năng xảy ra các kết quả không mong muốn và lỗi trong điều kiện đồng thời cao có thể nhanh chóng trở nên quá tải. Cố gắng giải quyết các vấn đề phức tạp và khó tái tạo đồng thời bằng cách áp dụng các gợi ý khóa ngẫu nhiên hoặc các phương pháp thử-và-sai khác có thể là một trải nghiệm cực kỳ khó chịu.

Theo nhiều khía cạnh, mức cô lập ảnh chụp nhanh dường như là một giải pháp hoàn hảo cho những vấn đề đồng thời này. Ý tưởng cơ bản là mỗi giao dịch ảnh chụp nhanh sẽ hoạt động như thể nó được thực thi dựa trên bản sao riêng tư của chính nó về trạng thái đã cam kết của cơ sở dữ liệu, được thực hiện tại thời điểm giao dịch bắt đầu. Việc cung cấp toàn bộ giao dịch với chế độ xem không thay đổi về dữ liệu đã cam kết rõ ràng đảm bảo kết quả nhất quán cho các hoạt động chỉ đọc, nhưng còn các giao dịch thay đổi dữ liệu thì sao?

Tính năng cô lập ảnh chụp nhanh xử lý các thay đổi dữ liệu một cách lạc quan, ngầm định rằng xung đột giữa những người viết đồng thời sẽ tương đối hiếm. Khi xung đột ghi xảy ra, người cam kết đầu tiên sẽ thắng và giao dịch thua cuộc có các thay đổi của nó được khôi phục lại. Tất nhiên, thật không may cho giao dịch quay lại, nhưng nếu điều này hiếm khi xảy ra, lợi ích của việc cách ly ảnh chụp nhanh có thể dễ dàng lớn hơn chi phí của việc thỉnh thoảng bị lỗi và thử lại.

Ngữ nghĩa tương đối đơn giản và rõ ràng của cách ly ảnh chụp nhanh (khi so sánh với các lựa chọn thay thế) có thể là một lợi thế đáng kể, đặc biệt đối với những người không làm việc riêng trong thế giới cơ sở dữ liệu và do đó không biết rõ các mức độ cách ly khác nhau. Ngay cả đối với các chuyên gia cơ sở dữ liệu dày dạn, mức độ cô lập tương đối 'trực quan' có thể là một giải pháp đáng hoan nghênh.

Tất nhiên, mọi thứ hiếm khi đơn giản như lần đầu xuất hiện, và tính năng cô lập ảnh chụp nhanh cũng không phải là ngoại lệ. Tài liệu chính thức thực hiện khá tốt việc mô tả những ưu điểm và nhược điểm chính của việc cô lập ảnh chụp nhanh, vì vậy phần lớn bài viết này tập trung vào việc khám phá một số vấn đề ít được biết đến và đáng ngạc nhiên mà bạn có thể gặp phải. Tuy nhiên, trước tiên, hãy xem nhanh các thuộc tính logic của mức cô lập này:

Thuộc tính ACID và Cách ly Ảnh chụp nhanh

Cách ly ảnh chụp nhanh không phải là một trong các mức cách ly được xác định trong Tiêu chuẩn SQL, nhưng nó vẫn thường được so sánh bằng cách sử dụng 'hiện tượng đồng thời' được xác định ở đó. Ví dụ:bảng so sánh sau được sao chép từ Bài báo kỹ thuật SQL Server, "Cách ly giao dịch dựa trên phiên bản hàng SQL Server 2005" của Kimberly L. Tripp và Neal Graves:

Bằng cách cung cấp chế độ xem theo thời điểm trong tổng số dữ liệu đã cam kết , cách ly ảnh chụp nhanh cung cấp khả năng bảo vệ chống lại cả ba hiện tượng đồng thời được hiển thị ở đó. Việc đọc bẩn bị ngăn chặn vì chỉ dữ liệu đã cam kết mới được hiển thị và bản chất tĩnh của ảnh chụp nhanh ngăn không cho cả việc đọc không lặp lại và các phantoms được gặp phải.

Tuy nhiên, sự so sánh này (và đặc biệt là phần được đánh dấu) chỉ cho thấy rằng các mức độ cô lập có thể nối tiếp và ảnh chụp nhanh ngăn ngừa ba hiện tượng cụ thể giống nhau. Nó không có nghĩa là chúng tương đương nhau về mọi mặt. Quan trọng là, tiêu chuẩn SQL-92 không xác định sự cô lập có thể tuần tự hóa chỉ riêng về ba hiện tượng. Phần 4.28 của tiêu chuẩn đưa ra định nghĩa đầy đủ:

Việc thực hiện các giao dịch SQL đồng thời ở mức cô lập SERIALIZABLE được đảm bảo là có thể tuần tự hóa. Một thực thi có thể tuần tự hóa được định nghĩa là một thực thi các hoạt động thực hiện đồng thời các giao dịch SQL tạo ra hiệu ứng tương tự như một số thực thi nối tiếp các giao dịch SQL tương tự đó. Thực thi nối tiếp là một thực thi trong đó mỗi giao dịch SQL thực thi để hoàn thành trước khi giao dịch SQL tiếp theo bắt đầu.

Mức độ và tầm quan trọng của các đảm bảo ngụ ý ở đây thường bị bỏ sót. Để nói nó bằng ngôn ngữ đơn giản:

Bất kỳ giao dịch có thể tuần tự hóa nào thực thi chính xác khi chạy một mình sẽ tiếp tục thực thi chính xác với bất kỳ sự kết hợp nào của các giao dịch đồng thời hoặc nó sẽ được khôi phục với thông báo lỗi (thường là bế tắc trong triển khai SQL Server).

Các mức cách ly không thể nối tiếp hóa, bao gồm cả cách ly ảnh chụp nhanh, không mang lại sự đảm bảo chắc chắn về tính đúng đắn.

Dữ liệu cũ

Việc cô lập ảnh chụp có vẻ gần như rất đơn giản. Các lần đọc luôn đến từ dữ liệu đã cam kết tại một thời điểm duy nhất và các xung đột ghi được tự động phát hiện và xử lý. Làm thế nào đây không phải là một giải pháp hoàn hảo cho tất cả các khó khăn liên quan đến đồng tiền?

Một vấn đề tiềm ẩn là các lần đọc ảnh chụp nhanh không nhất thiết phản ánh trạng thái đã cam kết hiện tại của cơ sở dữ liệu. Giao dịch ảnh chụp nhanh hoàn toàn bỏ qua mọi thay đổi đã cam kết được thực hiện bởi các giao dịch đồng thời khác sau khi giao dịch ảnh chụp nhanh bắt đầu. Một cách khác để nói điều đó là nói một giao dịch chụp nhanh thấy dữ liệu cũ, lỗi thời. Mặc dù hành vi này có thể là chính xác những gì cần thiết để tạo một báo cáo thời điểm chính xác, nhưng nó có thể không hoàn toàn phù hợp trong các trường hợp khác (ví dụ:khi được sử dụng để thực thi quy tắc trong trình kích hoạt).

Viết xiên

Việc cô lập ảnh chụp nhanh cũng dễ bị ảnh hưởng bởi một hiện tượng liên quan nào đó được gọi là lệch ghi. Đọc dữ liệu cũ đóng một vai trò trong việc này, nhưng vấn đề này cũng giúp làm rõ những gì ảnh chụp nhanh 'phát hiện xung đột ghi' làm và không làm.

Ghi lệch xảy ra khi hai giao dịch đồng thời, mỗi giao dịch đọc dữ liệu mà giao dịch kia sửa đổi. Không có xung đột ghi xảy ra vì hai giao dịch sửa đổi các hàng khác nhau. Không giao dịch nào nhìn thấy các thay đổi được thực hiện bởi bên kia, bởi vì cả hai đều đang đọc từ một thời điểm trước khi những thay đổi đó được thực hiện.

Một ví dụ cổ điển về viết xiên là bài toán đá cẩm thạch trắng và đen, nhưng tôi muốn đưa ra một ví dụ đơn giản khác ở đây:

-- Create two empty tables
CREATE TABLE A (x integer NOT NULL);
CREATE TABLE B (x integer NOT NULL);
 
-- Connection 1
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
INSERT A (x) SELECT COUNT_BIG(*) FROM B;
 
-- Connection 2
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
INSERT B (x) SELECT COUNT_BIG(*) FROM A;
COMMIT TRANSACTION;
 
-- Connection 1
COMMIT TRANSACTION;

Trong trường hợp cô lập ảnh chụp nhanh, cả hai bảng trong tập lệnh đó đều kết thúc bằng một hàng duy nhất chứa giá trị bằng không. Đây là một kết quả chính xác, nhưng nó không phải là một kết quả có thể tuần tự hóa:nó không tương ứng với bất kỳ lệnh thực hiện giao dịch nối tiếp nào có thể xảy ra. Trong bất kỳ lịch trình nối tiếp thực sự nào, một giao dịch phải hoàn thành trước khi giao dịch kia bắt đầu, do đó, giao dịch thứ hai sẽ tính hàng được chèn bởi giao dịch đầu tiên. Điều này nghe có vẻ như là một kỹ thuật, nhưng hãy nhớ rằng các đảm bảo có thể tuần tự hóa mạnh mẽ chỉ áp dụng khi các giao dịch thực sự có thể tuần tự hóa.

Tinh tế Phát hiện Xung đột

Xung đột ghi ảnh chụp nhanh xảy ra bất cứ khi nào một giao dịch ảnh chụp nhanh cố gắng sửa đổi một hàng đã được sửa đổi bởi một giao dịch khác đã cam kết sau khi giao dịch ảnh chụp nhanh bắt đầu. Có hai điều tinh tế ở đây:

  1. Các giao dịch thực sự không phải thay đổi bất kỳ giá trị dữ liệu nào; và
  2. Các giao dịch không phải sửa đổi bất kỳ cột chung nào .

Tập lệnh sau thể hiện cả hai điểm:

-- Test table
CREATE TABLE dbo.Conflict
(
    ID1 integer UNIQUE,
    Value1 integer NOT NULL,
    ID2 integer UNIQUE,
    Value2 integer NOT NULL
);
 
-- Insert one row
INSERT dbo.Conflict
    (ID1, ID2, Value1, Value2)
VALUES
    (1, 1, 1, 1);
 
-- Connection 1
BEGIN TRANSACTION;
 
UPDATE dbo.Conflict
SET Value1 = 1
WHERE ID1 = 1;
 
-- Connection 2
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
 
UPDATE dbo.Conflict
SET Value2 = 1
WHERE ID2 = 1;
 
-- Connection 1
COMMIT TRANSACTION;

Lưu ý những điều sau:

  • Mỗi giao dịch định vị cùng một hàng bằng cách sử dụng một chỉ mục khác nhau
  • Cả hai lần cập nhật đều không dẫn đến thay đổi đối với dữ liệu đã được lưu trữ
  • Hai giao dịch 'cập nhật' các cột khác nhau trong hàng.

Mặc dù vậy, khi giao dịch đầu tiên thực hiện thì giao dịch thứ hai sẽ kết thúc với lỗi xung đột cập nhật:

Tóm tắt:Tính năng phát hiện xung đột luôn hoạt động ở cấp độ của toàn bộ hàng và 'cập nhật' không thực sự phải thay đổi bất kỳ dữ liệu nào. (Trong trường hợp bạn thắc mắc, các thay đổi đối với dữ liệu LOB hoặc SLOB ngoài hàng cũng được tính là thay đổi đối với hàng nhằm mục đích phát hiện xung đột).

Vấn đề chính đối ngoại

Phát hiện xung đột cũng áp dụng cho hàng mẹ trong mối quan hệ khóa ngoại. Khi sửa đổi hàng con trong chế độ cô lập ảnh chụp nhanh, thay đổi đối với hàng mẹ trong một giao dịch khác có thể gây ra xung đột. Như trước đây, logic này áp dụng cho toàn bộ hàng cha - bản cập nhật gốc không phải ảnh hưởng đến chính cột khóa ngoại. Bất kỳ thao tác nào trên bảng con yêu cầu kiểm tra khóa ngoại tự động trong kế hoạch thực thi đều có thể dẫn đến xung đột không mong muốn.

Để chứng minh điều này, trước tiên hãy tạo các bảng và dữ liệu mẫu sau:

CREATE TABLE dbo.Dummy
(
    x integer NULL
);
 
CREATE TABLE dbo.Parent
(
    ParentID integer PRIMARY KEY,
    ParentValue integer NOT NULL
);
 
CREATE TABLE dbo.Child 
(
    ChildID integer PRIMARY KEY,
    ChildValue integer NOT NULL,
    ParentID integer NULL FOREIGN KEY REFERENCES dbo.Parent
);
 
INSERT dbo.Parent 
    (ParentID, ParentValue) 
VALUES (1, 1);
 
INSERT dbo.Child 
    (ChildID, ChildValue, ParentID) 
VALUES (1, 1, 1);

Bây giờ thực thi những điều sau từ hai kết nối riêng biệt như được chỉ ra trong các nhận xét:

-- Connection 1
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
SELECT COUNT_BIG(*) FROM dbo.Dummy;
 
-- Connection 2 (any isolation level)
UPDATE dbo.Parent SET ParentValue = 1 WHERE ParentID = 1;
 
-- Connection 1
UPDATE dbo.Child SET ParentID = NULL WHERE ChildID = 1;
UPDATE dbo.Child SET ParentID = 1 WHERE ChildID = 1;

Việc đọc từ bảng giả có ở đó để đảm bảo giao dịch chụp nhanh đã chính thức bắt đầu. Phát hành BEGIN TRANSACTION là không đủ để làm điều này; chúng tôi phải thực hiện một số loại truy cập dữ liệu trên bảng người dùng.

Bản cập nhật đầu tiên cho bảng Con không gây ra xung đột vì đặt cột tham chiếu thành NULL không yêu cầu kiểm tra bảng cha trong kế hoạch thực thi (không có gì để kiểm tra). Bộ xử lý truy vấn không chạm vào hàng mẹ trong kế hoạch thực thi, do đó, không có xung đột nào phát sinh.

Bản cập nhật thứ hai cho bảng Con không gây ra xung đột vì quá trình kiểm tra khóa ngoại được tự động thực hiện. Khi hàng Gốc được bộ xử lý truy vấn truy cập, hàng đó cũng được kiểm tra xem có xung đột cập nhật hay không. Lỗi xảy ra trong trường hợp này vì hàng Gốc được tham chiếu đã trải qua một sửa đổi được cam kết sau khi giao dịch ảnh chụp nhanh bắt đầu. Lưu ý rằng việc sửa đổi bảng Gốc không ảnh hưởng đến chính cột khóa ngoại.

Xung đột không mong muốn cũng có thể xảy ra nếu thay đổi đối với Bảng con tham chiếu đến hàng Gốc đã được tạo bằng một giao dịch đồng thời (và giao dịch đó được cam kết sau khi giao dịch chụp nhanh bắt đầu).

Tóm tắt:Một kế hoạch truy vấn bao gồm kiểm tra khóa ngoại tự động có thể gây ra lỗi xung đột nếu hàng được tham chiếu đã trải qua bất kỳ loại sửa đổi nào (bao gồm cả việc tạo!) Kể từ khi giao dịch ảnh chụp nhanh bắt đầu.

Vấn đề về bảng cắt ngắn

Một giao dịch chụp nhanh sẽ không thành công với lỗi nếu bất kỳ bảng nào mà nó truy cập đã bị cắt bớt kể từ khi giao dịch bắt đầu. Điều này áp dụng ngay cả khi bảng bị cắt ngắn không có hàng nào để bắt đầu, như tập lệnh bên dưới minh họa:

CREATE TABLE dbo.AccessMe
(
    x integer NULL
);
 
CREATE TABLE dbo.TruncateMe
(
    x integer NULL
);
 
-- Connection 1
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
SELECT COUNT_BIG(*) FROM dbo.AccessMe;
 
-- Connection 2
TRUNCATE TABLE dbo.TruncateMe;
 
-- Connection 1
SELECT COUNT_BIG(*) FROM dbo.TruncateMe;

SELECT cuối cùng không thành công với lỗi:

Đây là một tác dụng phụ khác cần kiểm tra trước khi bật tính năng cô lập ảnh chụp nhanh trên cơ sở dữ liệu hiện có.

Lần tới

Bài đăng tiếp theo (và cuối cùng) trong loạt bài này sẽ nói về mức độ cô lập không cam kết đã đọc (được gọi thân mật là "nolock").

[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. Tạo bảng mới trong IRI Workbench

  2. Lỗi T-SQL, cạm bẫy và các phương pháp hay nhất - hàm cửa sổ

  3. Chỉ mục đã lọc và Cột BAO GỒM

  4. Lược đồ Switch-A-Roo:Phần 2

  5. Làm thế nào để đánh số tầng trong SQL