可能由 Entity Framework 的页面锁定引起的死锁

Deadlock possibly caused by pagelock with Entity Framework

我们使用 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)

之后我们更新档案并保存更改。

在 运行 之后,测试客户端将针对同一请求但使用不同的 ID 多次并行向 api 发送垃圾邮件;我们注意到我们在 Persons 上遇到了僵局。

人物没有被重新用于档案,每个档案都有自己独特的一组人物。所以没有重叠的值被锁定。

查看死锁图后,我们注意到“Persons”上有一个页面锁。

据我了解,页面锁定会锁定额外的记录(即使是与查询无关的记录。)

这可能就是导致我们陷入僵局的原因。

现在我的问题是:为什么 sql 将一个简单的关系变成页锁而不是行锁? (每个档案的记录不会超过 10 人)。

这里最好的行动方案是什么?关闭升级并在 Persons with page lock disabled 的外键上添加索引?或者我们可能做错了什么?

在此先感谢您的输入。

可能有许多问题会触发此问题。第一个明显的是仔细检查所有相关实体是否具有完整且有效的 FK 关系,并且这些 FK 在 EF 中是 mapped/interpreted 正确的。如果这是 SQL 服务器中的 运行ning,要考虑的另一个选择是考虑启用快照隔离,允许 SQL 服务器更多地依赖行版本控制而不是锁。使用它的最大考虑是对 Temp DB 的影响,因此不一定推荐用于非常大的系统,特别是如果有大量批量操作 运行。

更新 Dossier 时,是否需要包含的每个相关实体?使用 EF 时的另一个常见问题是在满足对 DNRY(不要重复自己)的需求与性能之间找到平衡。通常这样的代码将作为 GetDossierById() 类型的方法驻留在 Repository 方法中,该方法在需要 Dossier 的任何地方和任何地方调用,并最终急切加载所有内容,因为其中一些领域(但不是全部)受益于拥有加载了额外的信息。对于高度并发使用的系统,例如可能有数百人在任何给定时间更新数据的 Web 应用程序,最好将其设计为使更新尽可能原子化。而不是给用户一个巨大的页面来一次性更新整个档案和与之相关的所有内容,然后用整个图表调用“提交”,将其分解以通过单独的操作提交更改。 IE。 AddPerson、RemovePerson 等。EF 或任何 ORM 需要一次加载以执行操作的数据越多,当系统加载更多行时 运行 陷入锁定问题的风险就越大每次都被“触摸”,并且操作通常需要更长的时间,从而使锁定时间更长。