实体框架死锁和并发

时间:2015-09-28 17:46:07

标签: .net sql-server vb.net entity-framework

我们在实体框架6和SqlSever 2012的数据库第一模型中大量使用Entity Framework。

我们有许多相当长的运行进程(10秒钟),每个进程创建一个具有不同数据的相同类型的对象,这些对象在创建时使用实体框架在数据库中写入和删除数据。到现在为止还挺好。为了提高应用程序的性能,我们希望并行运行这些操作,因此使用Task构造来实现如下:

Private Async Function LongRunningProcessAsync(data As SomeData) As Task(Of LongRunningProcessResult)
    Return Await Task.Factory.StartNew(Of LongRunningProcessResult)(Function()
                                                       Return Processor.DoWork(data)
                                                     End Function)             
End Function

我们运行其中的10个并等待所有这些使用Task.WaitAll

完成
Class Processor
    Public Function DoWork(data As SomeData) As LongRunningProcessResult
        Using context as new dbContext() 
           ' lots of database calls 
           context.saveChanges()
        end Using

        ' call to sub which creates a new db context and does some stuff
        doOtherWork()

        ' final call to delete temporary database data
        using yetAnotherContext as new dbContext()
            Dim entity = yetAnotherContext.temporaryData.single(Function(t) t.id = me.Id)
            yetAnotherContext.temporaryDataA.removeAll(entity.temporaryDataA)
            yetAnotherContext.temporaryDataB.removeAll(entity.temporaryDataB)
            yetAnotherContext.temporaryData.remove(entity)

            ' dbUpdateExecption Thrown here
            yetAnotherContext.SaveChanges()
        end using
    End Function
End Class

这很好用〜90%的时候其他10%的数据库服务器因内部死锁异常而死锁

所有处理器使用相同的表,但在进程之间绝对没有数据共享(并且不依赖于相同的FK行)并创建它们自己的实体框架上下文,它们之间没有共享交互。

回顾Sql Server实例的分析行为,我们在每个成功的查询之间看到大量非常短暂的锁定获取和释放。导致最终的僵局链:

Lock:Deadlock Chain Deadlock Chain SPID = 80 (e413fffd02c3)         
Lock:Deadlock Chain Deadlock Chain SPID = 73 (e413fffd02c3)     
Lock:Deadlock Chain Deadlock Chain SPID = 60 (6cb508d3484c) 

锁本身属于KEY类型,死锁查询全部用于同一个表,但具有不同的表格键:

exec sp_executesql N'DELETE [dbo].[temporaryData]
WHERE ([Id] = @0)',N'@0 int',@0=123

我们对实体框架相对较新,并且无法确定看似过度范围的锁的根本原因(我无法通过sql profiler识别被锁定的确切行)。

编辑:deadlock.xdl

EDIT2:在每个删除语句之后调用saveChanges删除死锁仍然不太明白为什么它会死锁

1 个答案:

答案 0 :(得分:8)

您似乎是锁定升级

的受害者

为了提高性能,Sql Server(以及所有现代数据库引擎)将许多低级细粒度锁转换为几个高级粗粒锁。在您的情况下,它超过阈值后从行级锁定到完整表锁定。您可以通过以下几种方式解决此问题:

  1. 一种解决方案是调用已经完成的SaveChanges()。 这将更快地释放锁,防止锁升级 发生,因为锁定次数减少=不太可能达到升级 阈值。
  2. 您还可以将隔离级别更改为读取未提交的,即 会通过允许脏读来降低锁的数量 还可以防止锁定升级。
  3. 最后,您应该能够使用SET发送ALTER TABLE命令 (LOCK_ESCALATION = {AUTO | TABLE | DISABLE})。然而表 即使禁用,仍然可以进行电平锁定。 MSDN指向 扫描没有聚集索引的表的示例 可序列化的隔离级别。看这里: https://msdn.microsoft.com/en-us/library/ms190273(v=sql.110).aspx
  4. 在您的情况下,您调用保存更改的解决方案会导致提交事务并且释放锁定是首选选项。