1

C# を使用して Visual Studio 2010 で Web アプリケーションを作成しています。Web アプリは複雑な SQL Server 2008 ステートメントを実行し、同じ .aspx ページが同時に複数回呼び出されると、デッドロックが発生することがあります。提案された解決策は、これらのデッドロックを防ぐために SQL サーバー手段を使用することですが、私の問題は、私がそれをよく理解していないことです。これは C# については正しくありません。

これらのデッドロックを防ぐために SQL サーバーを介してロックを行う代わりに、ASP.NET ページでロック (または C# でロック、またはミューテックスという名前の Windows) を使用することのマイナス面は何ですか?

PS。問題の SQL Server データベースは、この Web アプリケーションによってのみ使用されます。

編集:以下は、SQL ステートメントを実行する C# コードです。

int iNumRows = 0;

using (SqlConnection cn = new SqlConnection(strConnection))
{
    cn.Open();

    using (SqlCommand cmd = new SqlCommand(strSQL, cn))
    {
        //Use C# lock here
        iNumRows = Convert.ToInt32(cmd.ExecuteScalar());
        //Release C# lock here
    }
}

SQL のサンプルを次に示します (実際には、C# スクリプトによって動的に構成されます)。

SET XACT_ABORT ON;
BEGIN TRANSACTION;

DELETE FROM [dbo].[t_Log_2] 
      WHERE [idtm]<'2011-03-12 08:41:57';

WITH ctx AS(
     SELECT MIN([idtm]) AS mdIn, 
            MAX([odtm]) AS mdOut 
           FROM [dbo].[t_Log_2] 
          WHERE [type] = 0 
            AND [state] = 0 
            AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4' 
            AND [odtm] >= '2013-03-11 06:33:32' 
            AND [idtm] <= '2013-03-11 06:43:12' 
           ) 
INSERT INTO [dbo].[t_Log_2] 
([oid],[idtm],[odtm],[type],[state],[huid],
 [cnm],[cmdl],[batt],[dvtp0],[dvtp1]) 
SELECT 
    2, 
    CASE WHEN mdIn IS NOT NULL 
          AND mdIn < '2013-03-11 06:33:32' 
         THEN mdIn 
         ELSE '2013-03-11 06:33:32' 
         END,
    CASE WHEN mdOut IS NOT NULL 
          AND mdOut > '2013-03-11 06:43:12' 
         THEN mdOut 
         ELSE '2013-03-11 06:43:12' 
         END,
    0,
    0,
    N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4',
    null,
    null,
    0,
    1,
    null 
FROM ctx 

SELECT ROWCOUNT_BIG()

DELETE FROM [dbo].[t_Log_2] 
      WHERE [type] = 0 
        AND [state] = 0 
        AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4' 
        AND [odtm] >= '2013-03-11 06:33:32' 
        AND [idtm] <= '2013-03-11 06:43:12' 
        AND [id] <> SCOPE_IDENTITY()

DELETE FROM [dbo].[t_Log_2] 
      WHERE [type] = 0 
        AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4' 
        AND [idtm] >= (SELECT [idtm] FROM [dbo].[t_Log_2] 
                                    WHERE [id] = SCOPE_IDENTITY()) 
        AND [odtm] <= (SELECT [odtm] FROM [dbo].[t_Log_2] 
                                    WHERE [id] = SCOPE_IDENTITY()) 
        AND [id] <> SCOPE_IDENTITY() 

;WITH ctx1 AS( 
     SELECT [idtm] AS dI 
       FROM [dbo].[t_Log_2] 
      WHERE [id] = SCOPE_IDENTITY() 
             )
UPDATE [dbo].[t_Log_2] 
        SET [odtm] = ctx1.dI 
       FROM ctx1 
      WHERE [id] <> SCOPE_IDENTITY() 
        AND [type] = 0 
        AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4' 
        AND [idtm] < ctx1.dI 
        AND [odtm] > ctx1.dI 

;WITH ctx2 AS(
     SELECT [odtm] AS dO 
       FROM [dbo].[t_Log_2] 
      WHERE [id] = SCOPE_IDENTITY() 
             ) 
UPDATE [dbo].[t_Log_2] 
        SET [idtm] = ctx2.dO 
       FROM ctx2 
      WHERE [id] <> SCOPE_IDENTITY() 
        AND [type] = 0 
        AND [huid] = N'18ef4d56-6ef3-906a-a711-88d1bd6ab2d4' 
        AND [idtm] < ctx2.dO 
        AND [odtm] > ctx2.dO 

COMMIT TRANSACTION;
SET XACT_ABORT OFF
4

2 に答える 2

7

これらのデッドロックを防ぐために SQL サーバーを介してロックを行う代わりに、ASP.NET ページでロック (または C# でロック、またはミューテックスという名前の Windows) を使用することの欠点は何ですか?

デッドロックを引き起こす代わりに、ライブロックを引き起こします。

待機グラフにサイクル (A が B を待機、B が A を待機) が含まれている場合、デッドロックが発生します。SQL Server は定期的にすべての待機グラフを検査し、サイクルを探します。そのようなサイクルが 1 つ検出されると、被害者を選択してそのトランザクションを中止することでサイクルが中断されます。

これらのロックの一部を SQL Server 制御領域の外に移動すると、つまり. プロセス ミューテックス、クリティカル セクション、C# イベントなどでは、待機グラフ サイクルは引き続き発生しますが、サイクルはアプリを通じて完了するため、SQL Server では検出できなくなります (A は SQL で B を待機しますが、B は A を待機します)。アプリで)。デッドロック モニターはサイクルを認識しないため、デッドロック解決アルゴリズム (犠牲者を選択し、そのトランザクションを中止する) を実行せず、デッドロックは永久に残ります。おめでとうございます。デッドロック例外が発生する代わりに、アプリケーションが単にハングするようになりました。

それから私の言葉を鵜呑みにする必要はありません。他の経験豊富な人はすでにこの問題に悩まされ、難しい方法を学んでいますが、幸いなことにそれについて書いているので、簡単な方法を学ぶことができます. あなたが読んでいるまさにこのサイトはその一例です。

問題を理解すれば、SQL Server のデッドロックを解決するのはかなり簡単です。デッドロック グラフ(画像ではなく XML です!)をキャプチャして、テーブルの正確な定義と共に添付していただければ、おそらく私たちがお手伝いできます。悲しいかな、あなたはすでにそのような要求を無視しているので、尋ねる唯一の質問は、もっとロープが欲しいですか?

于 2013-03-13T07:40:15.847 に答える
1

十分な詳細を知らずに、実際にデッドロックを引き起こしている場所を見つけることができませんでした。おそらくキー範囲が原因でデッドロックが発生したと推測できます。つまり、テーブル t_log_2 のインデックスでデッドロックが発生したことを意味します。削除と更新があるため、間違いなくそうではありません。同じ行で発生しますが、同じキー範囲で発生する可能性があります。または、あるプロセスが A 範囲を保持し、B 範囲を要求し、別のプロセスが B 範囲を保持し、A 範囲を要求する可能性があります。SQL プロファイラーを使用してデッドロックを追跡し、デッドロックが正確に発生した場所を確認できます。または、単純に、パフォーマンスがそれほど損なわれない場合は、トランザクション分離レベルを [repeatable read] または [serializable] に設定できます。


SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
....
BEGIN TRANSACTION
....

于 2013-03-13T04:22:31.123 に答える