gpt4 book ai didi

.net - Entity Framework 死锁和并发

转载 作者:行者123 更新时间:2023-12-02 11:26:21 27 4
gpt4 key购买 nike

我们在具有 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 行),并创建所有自己的 Entity Framework 上下文,它们之间没有共享交互。

查看 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

我们对 Entity Framework 相对较新,无法确定出现范围过大的锁的根本原因(我无法通过 sql profiler 识别被锁定的确切行)。

编辑:deadlock.xdl

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

最佳答案

您似乎是锁定升级的受害者

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

  1. 一种解决方案是调用您已经完成的 SaveChanges() 。这将更快地释放锁,从而防止锁升级发生,因为较低的锁定计数 = 不太可能升级阈值。
  2. 您还可以将隔离级别更改为未提交读取,这通过允许脏读来减少锁的数量,这将还可以防止锁升级的发生。
  3. 最后,您应该能够使用 SET 发送 ALTER TABLE 命令( LOCK_ESCALATION = { 自动 | 表 | 禁用 } )。然而表即使禁用时,级别锁定仍然是可能的。 MSDN 指出扫描a下没有聚集索引的表的示例可串行化的隔离级别。看这里: https://msdn.microsoft.com/en-us/library/ms190273(v=sql.110).aspx

就您而言,调用保存更改的解决方案,导致事务被提交并释放锁是更好的选择。

关于.net - Entity Framework 死锁和并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32828871/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com