web-dev-qa-db-ja.com

SQL Serverでのデッドロックの防止

複数の行を1つに結合するアプリケーションをSQL Server 2014データベースに接続しています。アプリケーションの実行中は、このデータベースへの他の接続はありません。

まず、特定の期間内の行のチャンクを選択します。このクエリは、クラスター化ルックアップとマージされた非クラスター化シーク(TIME列)を使用します。

select ...
from FOO
where TIME >= @from and TIME < @to and ...

次に、これらの行をc#で処理し、変更を単一の更新と複数の削除として書き込みます。これは、チャンクごとに何度も発生します。これらも非クラスター化インデックスシークを使用します。

begin tran

update FOO set ...
where NON_CLUSTERED_ID = @id

delete FOO where NON_CLUSTERED_ID in (@id1, @id2, @id3, ...)

commit

これを複数の並列チャンクで実行すると、デッドロックが発生します。 ROWLOCKupdatedeleteを使用してみましたが、チャンク間にオーバーラップがなくても、何らかの理由で以前より多くのデッドロックが発生しました。

次に、TABLOCKX, HOLDLOCKupdateを使用しますが、selectを並列で実行できないため、並列処理の利点が失われます。

デッドロックを回避しながら複数の並列チャンクを処理する方法はありますか?

この場合、チャンク間に行の重複がない場合、NOLOCKselectで使用しても安全ですか?次にTABLOCKX, HOLDLOCKは、updatedeleteのみをブロックしますよね?

または、デッドロックが発生することを受け入れて、アプリケーションでクエリを再試行する必要がありますか?

[〜#〜] update [〜#〜](追加情報):これまでのすべてのデッドロックは、updateおよびdeleteフェーズで発生し、 select。今日、これを解決できない場合は、いくつかのデッドロックログを取得しようとします(以前は正しいトレースフラグが有効にされていませんでした)。

[〜#〜] update [〜#〜]:これらは、ROWLOCKで発生するデッドロックの2つの配置であり、どちらもdeleteステートメントのみを参照しますそれが使用する非クラスター化インデックス。これらがテーブルヒントなしで発生したデッドロックと同じであるかどうかは、再現できなかったためわかりません。

Deadlock 1Deadlock 2

.xdlから他に必要なものがあるかどうかを尋ねます。すべてを添付するのは少し疲れています。

5
Jussi Kosunen

このコードの複数のインスタンスが並行して実行されるのを防ぐために、更新トランザクションで sp_getapplock を使用します。これは、テーブルロックヒントのように選択ステートメントをブロックしません。

ロックの取得にはタイムアウトパラメータよりも長い時間がかかる場合があるため、再試行ロジックをプログラムする必要があります。

これは、更新トランザクションをsp_getapplockにラップする方法です。

BEGIN TRANSACTION;
BEGIN TRY

    DECLARE @VarLockResult int;
    EXEC @VarLockResult = sp_getapplock
        @Resource = 'some_unique_name_app_lock',
        @LockMode = 'Exclusive',
        @LockOwner = 'Transaction',
        @LockTimeout = 60000,
        @DbPrincipal = 'public';

    IF @VarLockResult >= 0
    BEGIN
        -- Acquired the lock
        update FOO set ...
        where NON_CLUSTERED_ID = @id

        delete FOO where NON_CLUSTERED_ID in (@id1, @id2, @id3, ...)

    END ELSE BEGIN
        -- return some error code, so that the caller could retry
    END;

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
    -- handle the error
END CATCH;

選択ステートメントに変更を加える必要はありません。

チャンク内のIDは重複しないと言っていても、NOLOCKはお勧めしません。このヒントにより、SELECTクエリは変更されている一部のページをスキップでき、一部のページを2回読み取ることができます。このような動作が許容される可能性はほとんどありません。

0