web-dev-qa-db-ja.com

同時呼び出しのCTEおよびOUTPUTブロックで更新

デッドロックの問題が発生したため、どこかで読んだこのCTEトリックを使い始めました。デッドロックはもうありません。

しかし、追加した各クライアントは、ストアドプロシージャの速度を低下させます(ブロックしますか?)。例えば。 1クライアントでの1〜2秒の更新は、2クライアントで2〜4秒になります。 (この単純なメタファークエリは0.001秒で実行されますが、少数のクライアントの後に0.03程度になるため、問題は実際の実装とは無関係です。)

ロックの問題ですか? (何らかの)トランザクションでラップする必要がありますか?

WITH UpdateView AS (
    SELECT TOP 1 W.*
    FROM [WidgetSandbox].[dbo].[Widgets] W
    INNER JOIN [WidgetSandbox].[dbo].[Sizes] S ON W.SizeId = S.Id
    WHERE W.StatusId = @availableStatusId
    AND W.ColorCode = @colorCode
    ORDER BY S.DiameterInches
)

UPDATE UpdateView 
SET StatusId = @soldOutStatusId
OUTPUT INSERTED.Id INTO @outputIds;

SET @singleUpdatedId = (SELECT TOP 1 Id FROM @outputIds);

SELECT * FROM [WidgetSandbox].[dbo].[Widgets]
WHERE Id = @singleUpdatedId;

特定の問題が何であるかをもっとよく理解できればいいのにと思っていますが、それだけで行き詰まっています...

DDL、それが役立つ場合: https://Gist.github.com/RobertBaldini/3740c7bb85eea47d7fe63cb8602ac2d6

リポジトリ: https://github.com/RobertBaldini/WidgetSandbox

5
Robert Baldini

表示される問題の多くは、非効率的な実行計画が原因です。

Supplied plan

提供されたプランとクエリが質問に一致するわけではありませんが、それでも、提供されたものを使用しています。

とにかく、私はあなたの 前の質問 で述べたName列のデータ型の変更(nvarchar(max)から)を実装する必要があります。さらに重要なことは、推奨するインデックスを追加するにする必要があるため、更新する行をソートせずに見つけることができます。

CREATE NONCLUSTERED INDEX IX_dbo_Sizes__DiameterInches
ON dbo.Sizes (DiameterInches);

CREATE NONCLUSTERED INDEX IX_dbo_Widgets__SizeId_ColorCode_StatusId__Name
ON dbo.Widgets (SizeId, ColorCode, StatusId)
INCLUDE (Name);

次に、選択したアイテムのステータスを更新し、影響を受ける行を返すことができます。

DECLARE
    -- Constant values guessed, replace with the real ones
    @availableStatusId integer = 1,
    @soldOutStatusId integer= 9,
    @colorCode nvarchar(6) = N'Red';

WITH UpdateView AS 
(
    SELECT TOP (1)
        W.Id,
        W.Name,
        W.StatusId,
        W.ColorCode,
        W.SizeId
    FROM dbo.Widgets AS W WITH (UPDLOCK, ROWLOCK, READPAST)
    JOIN dbo.Sizes AS S 
        ON W.SizeId = S.Id
    WHERE 
        W.StatusId = @availableStatusId
        AND W.ColorCode = @colorCode
    ORDER BY 
        S.DiameterInches
)
UPDATE UpdateView 
SET StatusId = @soldOutStatusId
OUTPUT
    Inserted.Id,
    Inserted.Name,
    Inserted.StatusId,
    Inserted.ColorCode,
    Inserted.SizeId;

そこにUPDLOCK, ROWLOCK, READPASTヒントがあることに注意してください。これらは、同時実行に必要な標準のヒントですFIFOキュータイプのテーブルへのアクセス。詳細については、「 キューとしてのテーブルの使用 Remus Rusanuによる」を参照してください。

(ソートなしで)取得する必要がある実行プランの形状は次のとおりです。

Update plan

余談ですが、十分な理由がない限り、データベースでSET AUTO_CLOSE ONを使用しないでください。

8
Paul White 9