假设我有一个执行以下操作的 Controller 操作:
简单的实现存在多个问题:
解决这些问题的方法似乎是使用SERIALIZABLE事务隔离级别。问题在于,每个人似乎都认为此事务隔离级别非常危险,因为它可能导致死锁。
给出以下简单的解决方案:
public class AController
{
// ...
public async Task Fn(..., CancellationToken cancellationToken)
{
var calendarSlotExists = dbContext.Slots.Where(...).AnyAsync(cancellationToken);
var appointmentsAreOverlapping = dbContext.Appointments.Where(...).AnyAsync(cancellationToken);
if (calendarSlotExists && !appointmentsAreOverlapping)
dbContext.Appointments.Add(...);
dbContext.SaveChangesAsync(cancellationToken);
}
}
始终避免并发问题的最佳方法是什么?我应该如何处理最终的死锁?
最佳答案
数据库完整性检查是您最好的 friend
根据您的描述,您的约会基于时段。这使问题变得更加简单,因为您可以在SlotId
表上有效地为Appointments
定义一个唯一约束。然后,您将需要一个用于Appointments.SlotId
引用Slot.Id
的外键
DB会抛出外键冲突异常
数据库将抛出重复的键异常
接下来,您需要捕获这两个异常(exception)并将用户重定向回预订页面。再次从数据库重新加载数据,并检查是否有无效条目,通知用户进行修改,然后重试。
对于死锁部分,它实际上取决于您的表结构。访问数据的方式,对数据进行索引的方式以及数据库的查询计划。没有确切的答案。