デッドロックが原因のSqlExceptionをキャッチする方法は?


92

.NET 3.5から/ C#のアプリは、私がキャッチしたいと思いますSqlExceptionが、それはデッドロックによって引き起こされている場合のみ、SQL Server 2008のインスタンスに。

一般的なエラーメッセージは Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

しかし、それはこの例外の文書化されたエラーコードではないようです。

メッセージ内のデッドロックキーワードの存在に対する例外のフィルタリングは、この動作を達成するための非常に醜い方法のようです。誰かがこれを行う正しい方法を知っていますか?


3
(最終的に)エラーコードのドキュメントを見つけました:msdn.microsoft.com/en-us/library/aa337376.aspx。:あなたはまた、SQL Server自体を介してこれを見つけることができますselect * from master.dbo.sysmessages where error=1205
マーティン・マクナルティ

回答:


153

デッドロックのMicrosft SQL Server固有のエラーコードは1205なので、SqlExceptionを処理して確認する必要があります。したがって、たとえば、他のすべてのタイプのSqlExceptionの場合は、バブルに例外を発生させます。

catch (SqlException ex)
{
    if (ex.Number == 1205)
    {
        // Deadlock 
    }
    else
        throw;
}

または、C#6で利用可能な例外フィルタリングを使用する

catch (SqlException ex) when (ex.Number == 1205)
{
    // Deadlock 
}

特定のメッセージの実際のSQLエラーコードを見つけるには、SQL Serverでsys.messagesを調べるのが便利です。

例えば

SELECT * FROM sys.messages WHERE text LIKE '%deadlock%' AND language_id=1033

(SQL Server 2005以降の)デッドロックを処理する別の方法は、TRY ... CATCHサポートを使用してストアドプロシージャ内で行うことです。

BEGIN TRY
    -- some sql statements
END TRY
BEGIN CATCH
    IF (ERROR_NUMBER() = 1205)
        -- is a deadlock
    ELSE
        -- is not a deadlock
END CATCH

完全にSQL内でデッドロック再試行ロジックを実装する方法の完全な例、MSDNにあります。


2
エラーコードはベンダー固有であるため、1205はSQL Serverのデッドロックですが、Oracle、MySQLなどでは異なる場合があります
brianmearns

3
データレイヤーによっては、SqlException別のレイヤーにラップされる場合があります。したがって、例外の種類をキャッチしてチェックする必要がある場合があります。それらが直接デッドロック例外でない場合は、再帰的にをチェックしInnerExceptionます。
フレデリック、

46

デッドロックを検出して、失敗した操作を再試行できるようにしたいと思うので、ちょっとした注意点について警告します。ここで少し外れたトピックで失礼します。

データベースによって検出されたデッドロックは、接続が.NETで開いたままの状態で、実行されているトランザクション(存在する場合)を効果的にロールバックします。その操作を(同じ接続で)再試行すると、トランザクションのないコンテキストで実行され、データが破損する可能性があります。

これに注意することが重要です。SQLが原因で障害が発生した場合に備えて、完全な接続を破壊することを検討することをお勧めします。操作の再試行は、トランザクションが定義されているレベルでのみ実行できます(トランザクションとその接続を再作成することにより)。

したがって、失敗した操作を再試行する場合は、完全に新しい接続を開いて、新しいトランザクションを開始してください。


4
なぜ完全に新しい接続が必要なのですか?この回答に関する質問をここに投稿しまし
2013年

3

デッドロックを検出するC#6の方法を次に示します。

try
{
    //todo: Execute SQL. 
    //IMPORTANT, if you used Connection.BeginTransaction(), this try..catch must surround that code. You must rollback the original transaction, then recreate it and re-run all the code.
}
catch (SqlException ex) when (ex.Number == 1205)
{
    //todo: Retry SQL
}

このtry..catchがトランザクション全体を囲んでいることを確認してください。@Steven(詳細については彼の回答を参照)によると、デッドロックが原因でsqlコマンドが失敗すると、トランザクションがロールバックされ、トランザクションを再作成しない場合、再試行は次のコンテキスト外で実行されます。トランザクションとデータの不整合が発生する可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.