我们在使用Entity Framework 6和SqlSever 2012的数据库优先模型中广泛使用Entity Framework。我们有一些相当长时间运行的进程(10多秒),每个进程都创建一个相同类型的对象,并使用不同的数据,在其创建过程中,这些对象都会使用Entity Framework在数据库中写入和删除数据。到目前为止还不错。为了提高应用程序的性能,我们正在尝试并行运行这些操作,因此我们使用“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行),并且创建它们自己的entityframework上下文而没有任何共享交互。
通过审查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分析器确定精确锁定的行)。
编辑:deadlock.xdl
编辑2:在每个删除语句后调用saveChanges
会消除死锁,但仍不太明白为什么死锁会发生。