Ngăn chặn bế tắc MERGE


9

Trong một trong các cơ sở dữ liệu của chúng tôi, chúng tôi có một bảng được truy cập đồng thời theo nhiều luồng. Chủ đề cập nhật hoặc chèn hàng qua MERGE. Thỉnh thoảng cũng có các luồng xóa hàng, vì vậy dữ liệu bảng rất dễ bay hơi. Chủ đề làm uperts đôi khi bị bế tắc. Vấn đề này trông giống như một mô tả trong câu hỏi này . Tuy nhiên, sự khác biệt là trong trường hợp của chúng tôi, mỗi luồng sẽ cập nhật hoặc chèn chính xác một hàng .

Thiết lập đơn giản là sau. Bảng này là đống với hai chỉ mục không bao gồm duy nhất trên

CREATE TABLE [Cache]
(
    [UID] uniqueidentifier NOT NULL CONSTRAINT DF_Cache_UID DEFAULT (newid()),
    [ItemKey] varchar(200) NOT NULL,
    [FileName] nvarchar(255) NOT NULL,
    [Expires] datetime2(2) NOT NULL,
    CONSTRAINT [PK_Cache] PRIMARY KEY NONCLUSTERED ([UID])
)
GO
CREATE UNIQUE INDEX IX_Cache ON [Cache] ([ItemKey]);
GO

và truy vấn điển hình là

DECLARE
    @itemKey varchar(200) = 'Item_0F3C43A6A6A14255B2EA977EA730EDF2',
    @fileName nvarchar(255) = 'File_0F3C43A6A6A14255B2EA977EA730EDF2.dat';

MERGE INTO [Cache] WITH (HOLDLOCK) T
USING (
    VALUES (@itemKey, @fileName, dateadd(minute, 10, sysdatetime()))
) S(ItemKey, FileName, Expires)
ON T.ItemKey = S.ItemKey
WHEN MATCHED THEN
    UPDATE
    SET
        T.FileName = S.FileName,
        T.Expires = S.Expires
WHEN NOT MATCHED THEN
    INSERT (ItemKey, FileName, Expires)
    VALUES (S.ItemKey, S.FileName, S.Expires)
OUTPUT deleted.FileName;

tức là, kết hợp xảy ra bởi khóa chỉ mục duy nhất. Gợi ý HOLDLOCKở đây, vì đồng thời (như được khuyên ở đây ).

Tôi đã làm điều tra nhỏ và sau đây là những gì tôi tìm thấy.

Trong hầu hết các trường hợp kế hoạch thực hiện truy vấn là

chỉ mục tìm kiếm kế hoạch thực hiện

với mẫu khóa sau

chỉ số tìm kiếm mô hình khóa

tức là IXkhóa trên đối tượng theo sau là khóa chi tiết hơn.

Tuy nhiên, đôi khi, kế hoạch thực hiện truy vấn là khác nhau

kế hoạch thực hiện quét bảng

(hình dạng kế hoạch này có thể được ép buộc bằng cách thêm INDEX(0)gợi ý) và mô hình khóa của nó là

mô hình khóa bảng quét

Xkhóa thông báo được đặt trên đối tượng sau khi đã IXđược đặt.

Vì hai IXlà tương thích, nhưng hai Xlà không, điều xảy ra theo đồng thời là

bế tắc

đồ thị bế tắc

Bế tắc !

Và đây là phần đầu tiên của câu hỏi phát sinh. Là đặt Xkhóa trên đối tượng sau khi IXđủ điều kiện? Nó không phải là lỗi?

Tài liệu nêu rõ:

Khóa ý định được đặt tên là khóa mục đích vì chúng có được trước khi khóa ở cấp thấp hơn và do đó báo hiệu ý định đặt khóa ở mức thấp hơn .

cũng

IX có nghĩa là ý định chỉ cập nhật một số hàng chứ không phải tất cả các hàng

vì vậy, đặt Xkhóa trên đối tượng sau khi IXtrông RẤT đáng nghi với tôi.

Đầu tiên tôi đã cố gắng ngăn chặn bế tắc bằng cách cố gắng thêm gợi ý khóa bảng

MERGE INTO [Cache] WITH (HOLDLOCK, TABLOCK) T

MERGE INTO [Cache] WITH (HOLDLOCK, TABLOCKX) T

với TABLOCKmô hình khóa tại chỗ trở thành

hợp nhất mô hình khóa tablock

và với TABLOCKXmẫu khóa là

hợp nhất mô hình khóa tablockx

vì hai SIX(cũng như hai X) không tương thích, điều này ngăn chặn bế tắc một cách hiệu quả, nhưng, thật không may, cũng ngăn ngừa đồng thời (điều không mong muốn).

Những nỗ lực tiếp theo của tôi là thêm PAGLOCKROWLOCKlàm cho khóa trở nên chi tiết hơn và giảm sự tranh chấp. Cả hai đều không có tác dụng ( Xtrên đối tượng vẫn được quan sát ngay sau đó IX).

Nỗ lực cuối cùng của tôi là buộc hình dạng kế hoạch thực hiện "tốt" với khóa hạt tốt bằng cách thêm FORCESEEKgợi ý

MERGE INTO [Cache] WITH (HOLDLOCK, FORCESEEK(IX_Cache(ItemKey))) T

va no đa hoạt động.

Và đây là phần thứ hai của câu hỏi phát sinh. Nó có thể xảy ra FORCESEEKsẽ bị bỏ qua và mô hình khóa xấu sẽ được sử dụng? (Như tôi đã đề cập, PAGLOCKROWLOCKdường như bị bỏ qua).


Thêm UPDLOCKkhông có tác dụng ( Xtrên đối tượng vẫn có thể quan sát sau IX).

Làm cho IX_Cachechỉ số co cụm, như dự đoán, làm việc. Nó đã dẫn đến kế hoạch với Tìm kiếm chỉ số cụm và khóa hạt. Ngoài ra, tôi đã cố gắng ép Clustered Index Scan cũng cho thấy khóa hạt.

Tuy nhiên. Quan sát bổ sung. Trong thiết lập ban đầu ngay cả khi có FORCESEEK(IX_Cache(ItemKey)))tại chỗ, nếu một thay đổi @itemKeykhai báo biến từ varchar (200) sang nvarchar (200) , kế hoạch thực hiện sẽ trở thành

chỉ mục tìm kiếm kế hoạch thực hiện với nvarchar

thấy rằng tìm kiếm được sử dụng, mẫu khóa NHƯNG trong trường hợp này một lần nữa hiển thị Xkhóa được đặt trên đối tượng sau IX.

Vì vậy, có vẻ như việc tìm kiếm buộc không nhất thiết phải đảm bảo các khóa hạt (do đó không có sự bế tắc). Tôi không tự tin rằng việc có chỉ số phân cụm đảm bảo khóa hạt. Hay không?

Sự hiểu biết của tôi (sửa tôi nếu tôi sai) là việc khóa là tình huống ở một mức độ lớn và hình dạng kế hoạch thực hiện nhất định không bao hàm mô hình khóa nhất định.

Câu hỏi về điều kiện đặt Xkhóa trên đối tượng sau khi IXvẫn mở. Và nếu nó đủ điều kiện, có điều gì mà người ta có thể làm để ngăn chặn việc khóa đối tượng không?


Yêu cầu liên quan tại feedback.azure.com
i-one

Câu trả lời:


9

Là đặt IXtheo sau Xtrên đối tượng đủ điều kiện? Có lỗi hay không?

Có vẻ hơi kỳ lạ, nhưng nó là hợp lệ. Tại thời điểm IXđược thực hiện, ý định có thể là để Xkhóa ở mức thấp hơn. Không có gì để nói rằng những khóa như vậy thực sự phải được thực hiện. Rốt cuộc, có thể không có gì để khóa ở cấp thấp hơn; động cơ không thể biết rằng trước thời hạn. Ngoài ra, có thể có các tối ưu hóa sao cho các khóa cấp thấp hơn có thể bị bỏ qua (một ví dụ cho ISvà các Skhóa có thể được nhìn thấy ở đây ).

Cụ thể hơn cho kịch bản hiện tại, đúng là các khóa phạm vi khóa tuần tự hóa không có sẵn cho một đống, vì vậy thay thế duy nhất là một Xkhóa ở cấp đối tượng. Theo nghĩa đó, động cơ có thể có thể sớm phát hiện ra rằng Xkhóa chắc chắn sẽ được yêu cầu nếu phương thức truy cập là quét heap, và do đó tránh lấy IXkhóa.

Mặt khác, khóa rất phức tạp và đôi khi khóa mục đích có thể được thực hiện vì lý do nội bộ không nhất thiết liên quan đến ý định lấy khóa cấp thấp hơn. Lấy IXcó thể là cách ít xâm lấn nhất để cung cấp sự bảo vệ cần thiết cho một số trường hợp cạnh khó hiểu. Đối với một loại xem xét tương tự, xem Khóa chia sẻ được phát hành trên IsolationLevel.ReadUncommned .

Vì vậy, tình huống hiện tại thật không may cho kịch bản bế tắc của bạn và về nguyên tắc có thể tránh được, nhưng điều đó không nhất thiết giống như là một 'lỗi'. Bạn có thể báo cáo vấn đề thông qua kênh hỗ trợ thông thường hoặc trên Microsoft Connect, nếu bạn cần câu trả lời dứt khoát về vấn đề này.

Nó có thể xảy ra FORCESEEKsẽ bị bỏ qua và mô hình khóa xấu sẽ được sử dụng?

Số FORCESEEKlà ít gợi ý và nhiều chỉ thị. Nếu trình tối ưu hóa không thể tìm thấy kế hoạch tôn vinh 'gợi ý', nó sẽ tạo ra lỗi.

Buộc chỉ mục là một cách để đảm bảo rằng các khóa phạm vi khóa có thể được thực hiện. Cùng với các khóa cập nhật được thực hiện một cách tự nhiên khi xử lý một phương thức truy cập để các hàng thay đổi, điều này cung cấp đủ sự đảm bảo để tránh các vấn đề tương tranh trong kịch bản của bạn.

Nếu lược đồ của bảng không thay đổi (ví dụ: thêm một chỉ mục mới), gợi ý cũng đủ để tránh truy vấn này bế tắc với chính nó. Vẫn có khả năng xảy ra bế tắc theo chu kỳ với các truy vấn khác có thể truy cập vào heap trước chỉ mục không được bao gồm (chẳng hạn như cập nhật cho khóa của chỉ mục không được bao gồm).

... khai báo biến từ varchar(200)đến nvarchar(200)...

Điều này phá vỡ sự đảm bảo rằng một hàng duy nhất sẽ bị ảnh hưởng, do đó, Bộ đệm bảng Eager được giới thiệu để bảo vệ Halloween. Như một cách giải quyết khác cho việc này, hãy đảm bảo rõ ràng với MERGE TOP (1) INTO [Cache]....

Sự hiểu biết của tôi [...] là việc khóa là tình huống ở một mức độ lớn và hình dạng kế hoạch thực hiện nhất định không bao hàm mô hình khóa nhất định.

Chắc chắn có nhiều hơn nữa đang diễn ra trong một kế hoạch thực hiện. Bạn có thể buộc một hình dạng kế hoạch nhất định với ví dụ như hướng dẫn kế hoạch, nhưng động cơ vẫn có thể quyết định thực hiện các khóa khác nhau khi chạy. Cơ hội khá thấp nếu bạn kết hợp các TOP (1)yếu tố trên.

Nhận xét chung

Thật là bất thường khi thấy một bảng heap được sử dụng theo cách này. Bạn nên xem xét ưu điểm của việc chuyển đổi nó thành một bảng cụm, có lẽ sử dụng chỉ mục Dan Guzman đề xuất trong một nhận xét:

CREATE UNIQUE CLUSTERED INDEX IX_Cache ON [Cache] ([ItemKey]);

Điều này có thể có lợi thế tái sử dụng không gian quan trọng, cũng như cung cấp một cách giải quyết tốt cho vấn đề bế tắc hiện tại.

MERGEcũng hơi bất thường khi thấy trong một môi trường đồng thời cao. Hơi phản tác dụng, thường hiệu quả hơn khi thực hiện các câu lệnh INSERTUPDATEcâu lệnh, ví dụ:

DECLARE
    @itemKey varchar(200) = 'Item_0F3C43A6A6A14255B2EA977EA730EDF2',
    @fileName nvarchar(255) = 'File_0F3C43A6A6A14255B2EA977EA730EDF2.dat';

BEGIN TRANSACTION;

    DECLARE @expires datetime2(2) = DATEADD(MINUTE, 10, SYSDATETIME());

    UPDATE TOP (1) dbo.Cache WITH (SERIALIZABLE, UPDLOCK)
    SET [FileName] = @fileName,
        Expires = @expires
    OUTPUT Deleted.[FileName]
    WHERE
        ItemKey = @itemKey;

    IF @@ROWCOUNT = 0
        INSERT dbo.Cache
            (ItemKey, [FileName], Expires)
        VALUES
            (@itemKey, @fileName, @expires);

COMMIT TRANSACTION;

Lưu ý cách tra cứu RID không còn cần thiết:

Kế hoạch thực hiện

Nếu bạn có thể đảm bảo sự tồn tại của một chỉ mục duy nhất trên ItemKey(như trong câu hỏi), phần dư thừa TOP (1)trong UPDATEcó thể được loại bỏ, đưa ra kế hoạch đơn giản hơn:

Cập nhật đơn giản hóa

Cả hai INSERTUPDATEkế hoạch đủ điều kiện cho một kế hoạch tầm thường trong cả hai trường hợp. MERGEluôn yêu cầu tối ưu hóa đầy đủ dựa trên chi phí.

Xem vấn đề đầu vào đồng thời Q & A SQL Server 2014 để biết mẫu chính xác sẽ sử dụng và biết thêm thông tin về MERGE.

Bế tắc không thể luôn luôn được ngăn chặn. Chúng có thể được giảm đến mức tối thiểu với mã hóa và thiết kế cẩn thận, nhưng ứng dụng phải luôn được chuẩn bị để xử lý bế tắc lẻ một cách duyên dáng (ví dụ: kiểm tra lại các điều kiện sau đó thử lại).

Nếu bạn có toàn quyền kiểm soát các quy trình truy cập vào đối tượng được đề cập, bạn cũng có thể xem xét sử dụng các khóa ứng dụng để tuần tự hóa quyền truy cập vào các phần tử riêng lẻ, như được mô tả trong SQL Server Chèn và xóa đồng thời .

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.