タイプごとにテーブルを持つエンティティフレームワークを使用しています。返されているオブジェクトのタイプの識別子列を持つins_GameAppInstancesというテーブルがあります。 ins_DefaultBankAccountInstancesと呼ばれるins_GameAppInstancesへの外部キーを持ついくつかの設定を持つ別のテーブルがあります。
データベースのスループットが高い場合、selectサブクエリを使用した選択と同じins_GameAppInstanceテーブルの更新との間でデッドロックが発生しています。もちろん、SQLサーバーは、犠牲者として選択を強制終了することを選択しています。
私はここまで頭を悩ませています。ページロックを見ると、ページID 6122があり、次にページID 4537があります。これは、最初の選択ロックがSでロックされ、更新がサブクエリ選択とIXでロックされた後、サブクエリが後のページをロックするために発生する可能性があります。 ?これはデッドロックを引き起こしますか?この混乱を引き起こしているのは本当にサブクエリの選択ですか?
この更新では、選択クエリの結果に関連する更新は行われないことがわかっています。 SQL ServerでREAD_COMMITTEDを許可するだけでも安全でしょうか。 Entity Frameworkとエンティティデータマッピングのため、ドメインロジックの動作を変更したくありません。
更新:私は最後の手段として、try catch sql例外を取得することで対処できると考えていました。例外をスタックにスローします。コンソールアプリを使用して統合テストを実行すると、dbを破壊するだけで、デッドロックが発生するまでかなりの待ち時間が発生します。
catch (SqlException ex)
{
if (ex.Number == 1205)
{
if (LogManager.Enabled)
LogManager.GetLogger(GetType()).Debug("Deadlock");
}
else
throw;
}
これがins_GameAppInstanceテーブルです
これがDefaultBankAccountInstanceテーブルです。 GameAppInstanceIDは、ins_GameAppInstancesの外部キーです。
これが起こっているデッドロックです。左側はリクエストと所有者S、右側はIXリクエストと所有者。
エンティティフレームワークが構築している選択と選択のサブクエリを次に示します。
exec sp_executesql N'SELECT
[Join1].[CurrentWeek] AS [CurrentWeek],
[Join1].[Discriminator] AS [Discriminator],
[Join1].[ID1] AS [ID],
[Join1].[Name] AS [Name],
[Join1].[WeekHasStarted] AS [WeekHasStarted],
[Join1].[Created1] AS [Created],
[Join1].[GameAppID] AS [GameAppID],
[Join1].[GameInstanceID] AS [GameInstanceID],
[Join1].[ID2] AS [ID1]
FROM [dbo].[ins_GameAppInstances] AS [Extent1]
INNER JOIN (SELECT [Extent2].[ID] AS [ID1], [Extent2].[Name] AS [Name], [Extent2].[CurrentWeek] AS [CurrentWeek], [Extent2].[WeekHasStarted] AS [WeekHasStarted], [Extent2].[Created] AS [Created1], [Extent2].[Discriminator] AS [Discriminator], [Extent2].[GameAppID] AS [GameAppID], [Extent2].[GameInstanceID] AS [GameInstanceID], [Extent3].[ID] AS [ID2]
FROM [dbo].[ins_GameAppInstances] AS [Extent2]
LEFT OUTER JOIN [dbo].[ins_DefaultBankAccountInstances] AS [Extent3] ON [Extent2].[ID] = [Extent3].[GameAppInstanceID] ) AS [Join1] ON [Extent1].[ID] = [Join1].[ID1]
WHERE ([Extent1].[GameInstanceID] = @EntityKeyValue1) AND ([Join1].[Discriminator] IN (N''BankingAppInstance'',N''BillsAppInstance'',N''CashboxAppInstance'',N''CreditCardsAppInstance'',N''EmailsAppInstance'',N''ExpensesAppInstance'',N''RandomEventsAppInstance'',N''TextMessagesAppInstance'',N''GameAppInstance''))',N'@EntityKeyValue1 uniqueidentifier',@EntityKeyValue1='55E2A02B-73AF-4CC9-BC09-7BD46473CF4F'
同時に送信される更新は次のとおりです。
exec sp_executesql N'UPDATE [dbo].[ins_GameAppInstances]
SET [CurrentWeek] = @0, [WeekHasStarted] = @1
WHERE ([ID] = @2)
',N'@0 int,@1 bit,@2 uniqueidentifier',@0=4,@1=0,@2='FD00CB51-A8DD-4705-87E9-B1DD34322264'
XMLデッドロックファイルの更新
私の経験から、READ COMMITTEDを使用し、同時実行性が高い場合、デッドロックが発生します。デッドロックの頻度は、トランザクションの並行性と長さとともに増加します。
トランザクション分離レベルを下げると、SELECTステートメントが原因でデッドロックが発生しなくなることまで改善できますが、それでも、UPDATEステートメントが原因でデッドロックが発生する可能性があります。
最善の解決策は、デッドロックが発生する可能性があり、発生することを想定して、特定の回数だけ再試行できるようにすることです。