带有 SELECT 子查询的 SELECT 会导致 SQL Server 中的更新查询死锁吗?

5 sql-server deadlock c#

我正在使用带有每种类型表的实体框架。我有一个名为 ins_GameAppInstances 的表,它有一个用于我返回的对象类型的鉴别器列。还有另一个表有一些设置,这些设置有一个称为 ins_DefaultBankAccountInstances 的 ins_GameAppInstances 外键。

当我对数据库有高吞吐量时,在带有选择子查询的选择和对同一个 ins_GameAppInstance 表的更新之间会发生死锁。sql server 选择杀死 select 当然作为受害者。

我正在绞尽脑汁到这一步。如果您查看页面锁,会发现页面 ID 6122 和页面 ID 4537。这可能是因为第一个选择锁定在 S 上,更新介于子查询选择和 IX 上的锁定之间,然后子查询锁定后面的页面? 这导致死锁?真的是子查询选择导致了这种混乱?

我知道更新不会更新与选择查询结果有关的任何内容。在 SQL Server 中只允许 READ_COMMITTED 是否安全。我真的不想这样做,但我也不想因为实体框架和实体数据映射而改变我的域逻辑的工作方式。

更新:我在想,作为最后的手段,我可​​以通过抓住一个 try catch sql 异常来处理它。将异常向上抛出堆栈。当我使用控制台应用程序运行我的集成测试只是粉碎数据库时,虽然发生死锁需要等待很长时间。

catch (SqlException ex)
        {
            if (ex.Number == 1205)
            {
                if (LogManager.Enabled)
                    LogManager.GetLogger(GetType()).Debug("Deadlock");
            }
            else
                throw;
        }
Run Code Online (Sandbox Code Playgroud)

这是 ins_GameAppInstance 表

游戏应用实例

这是 DefaultBankAccountInstance 表。GameAppInstanceID 是 ins_GameAppInstances 的外键。

DefaultBankAccountInstanceTable

这是正在发生的僵局。左侧是请求和所有者 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'
Run Code Online (Sandbox Code Playgroud)

这是同时发送的更新:

    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'
Run Code Online (Sandbox Code Playgroud)

更新 XML 死锁文件

http://pastebin.com/mRNm0Rje

小智 1

根据我的经验,当你使用READ COMMITTED并且并发度很高时,就会发生死锁。死锁的频率会随着并发数和事务长度的增加而增加。

降低事务隔离级别可以改善情况,使您不会因 SELECT 语句而出现死锁,但仍然有可能因 UPDATE 语句而出现死锁。

最好的解决方案是假设死锁是可能的并且将会发生,并允许一定次数的重试。