我们使用LINQ - Entity Framework -SQL获得了以下查询:
Context.Dossiers
.Include(x => x.Persons)
.ThenInclude(x=> x.Address)
.Include(x => x.Visits)
.Include(x => x.Questions)
.Include(x => x.Signatures)
.Include(x => x.Areas)
.Include(x => x.Zones)
.FirstOrDefaultAsync(x => x.Id == id)之后,我们更新档案并保存更改。
在运行了一个测试客户端,该客户端将为相同的请求并行多次向api发送垃圾邮件,但是使用了不同的in;我们注意到Person上出现了死锁。
人员不会重用于档案,并且每个档案都有其自己的唯一人员集。所以没有重叠值被锁定。
在查看死锁图表后,我们注意到“Person”上有一个页面锁定。
据我所知,页面锁定将锁定额外的记录(即使是与查询无关的记录)。
所以这可能就是导致我们陷入僵局的原因。
现在我的问题是:为什么sql把一个简单的关系变成一个页锁,而不是一个行锁?(每个档案不会有超过10个人的记录)。
这里最好的行动方案是什么?是否关闭升级并在禁用页面锁定的人员的外键上添加索引?或者我们可能做错了什么?
提前感谢您的投入。
发布于 2021-10-18 21:34:43
可能有许多问题会触发此问题。第一个显而易见的问题是仔细检查所有相关实体之间是否具有完整有效的FK关系,并且这些FK在EF中被正确映射/解释。如果这在SQL Server中运行,需要考虑的另一个选项是启用快照隔离,从而允许SQL Server更多地依赖行版本控制而不是锁。使用它的最大考虑因素是对Temp DB的影响,因此对于非常大的系统,尤其是在运行大型批量操作的情况下,不一定建议这样做。
当您更新档案时,是否需要每个包含的相关实体?使用EF时的另一个常见问题是在满足DNRY (不要重复自己)的愿望和性能之间找到平衡。通常,这样的代码将作为GetDossierById()-type方法驻留在Repository方法中,该方法在需要档案的任何地方和任何地方被调用,并最终急切地加载所有内容,因为这些区域中的一些,但不是所有,都从加载额外信息中受益。对于高并发使用的系统,例如web应用程序,可能有数百人在任何给定时间更新数据,最好将其设计为尽可能使更新成为原子的。而不是为用户提供一个庞大的页面来一次性更新整个档案和与之相关的所有内容,然后调用整个图的" submit“,通过单独的操作将其分解以提交更改。EF或任何RemovePerson一次需要加载的数据越多,当系统在负载下运行时遇到锁定问题的风险就越大,因为每次“触摸”更多的行,并且操作通常需要更长的时间,从而使锁定时间更长。
https://stackoverflow.com/questions/69619563
复制相似问题