私はMSSQLの値をインクリメントしようとしています(例、ギフトカードを購入した後にユーザーの残高を増やす)。私は単純化のためテーブル内の1つの行を持っていると私はすべての行をインクリメント単一テーブルストアドプロシージャデッドロック
CREATE TABLE Test_Table ([intCount] [int] NOT NULL)
ALTER PROCEDURE Test_Proc AS
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
UPDATE Test_Table
SET intCount = intCount + 1
COMMIT TRAN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
RETURN(0)
END
:
私のストアドプロシージャとテーブルは次のようになり。
私はC#で10スレッドを生成し、各スレッドでストアドプロシージャを10回呼び出します。しかし、ほとんどのスレッドでデッドロックが発生します。
for (int thread = 0; thread < threads; thread++)
{
new Thread(() =>
{
try
{
for (int ix = 0; ix < count; ix++)
{
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("Test_Proc", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
}
}
}
catch (Exception e)
{
errors++;
}
}).Start();
}
私が(UPDLOCK、HOLDLOCK)で試みたが、それは、デッドロックの頻度を減らすようには見えなかった。このストアドプロシージャを呼び出す私のコードは次のようになります。
私はこれらのデッドロックを防ぐためにストアドプロシージャを変更できますか?私は本当にC#ですべてのストアドプロシージャ呼び出しをシリアライズするのではなく、SQLの答えを探しています。
(これはDeadlock with single stored procedure and multiple threadsと似ていますが、デッドロックを回避したいと思っている間は、デッドロックの方法を明示的に求めています)。
EDIT:ストアドプロシージャの内容をトランザクションに入れるようにコードを変更しましたが、それでもデッドロックが発生しました。
EDIT:エラーメッセージはTransaction (Process ID 124) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
のように見えます。
EDIT:フィードバックごとにストアドプロシージャを更新しましたが、それでもデッドロックが発生します。
EDIT:説明は次のとおりです:https://stackoverflow.com/a/36831413/1117119。 SERIALIZABLEは共有ロックを取得し、書き込みを行うときに排他ロックに変換します。これにより、デッドロックが発生します。
また、デッドロックが発生するのは、単にスレッドが多すぎるためです(SQL Server 2008: Getting deadlocks... without any locks)。
これをチェックすると、同じルートの問題であるように見えます(http://stackoverflow.com/a/41594231/1158842)。SQLレベルでそれを実行したい場合は、sp_getapplockに向かう可能性があります。 –
最初に現在の値を '選択 'する必要はありません。 UPDATE Test_Table SET intCount = intCount + 1'で十分です。あなたは「*デッドロック*」と言っていますが、実際には、SQL Serverがデッドロックを検出したために1つのトランザクションが強制終了されたという例外がありますか?はいの場合は、あなたの質問にその例外のエラーメッセージを追加してください。同時に複数のトランザクションから同じ行を更新することはできないため、スレッドは待機し、2番目の更新は常に前の更新が完了するまで待つ必要があるため待機します。 –