如何处理 SQL Server 的并发问题?

问题描述 投票:0回答:1

我在使用 SQL Server 和实体框架的 .NET 6 项目中遇到问题。我的应用程序部署在 Kubernetes 上,因此有 3 个 Pod。我有多个用户尝试从数据库中获取下一个可用的“订单”记录(用户名= null,状态=“可用”)。当用户获取下一个订单时,他们会将其用户名分配给订单记录,然后使用

await _dbContext.SaveChangesAsync()
更新记录以反映该分配。我希望每个用户都有一个唯一的订单来查看,但有些用户获得相同的订单记录,从而导致并发问题。

我已经在捕获

DbUpdateConcurrencyException
的 Try/Catch 块中进行了保存,这似乎可以防止大多数并发问题。然而,仍然有一些操作没有被双重更新操作捕获并成功。这些操作在同一时间(不到 0.2 秒)在不同的 Pod 上发生。

日志截图

var nextAvailableOrder = await _dbContext.Orders
      .Where(o => o.Status == OrderStatus.Available && o.Username == null)
      .OrderByDescending(o => o.Type)
      .ThenBy(o => o.DateOfService)
      .FirstOrDefaultAsync(cancellationToken);

if (nextAvailableOrder == null)
{
    return null;
}
try
{
    // Assign new user and transition Order to new status
    // Order.Username = request.Username
    // Order.Status = 'In Progress'
    _stateEngine.MoveNext(nextAvailableOrder, OrderAction.GetNext, request.Username);
    await _dbContext.SaveChangesAsync(cancellationToken);

    return nextAvailableOrder;
}
                
   // Handle concurrency issues
   catch (DbUpdateConcurrencyException ex)
{
// Retry logic
}

我使用订单记录上的用户名字段作为并发令牌,但并行更新的问题仍然存在。欢迎任何建议。

c# sql-server entity-framework concurrency
1个回答
0
投票

您遇到的问题是,您对 SQL 进行了 2 次单独的调用,一次调用是为了查找下一项,另一次调用是为了更新用户名/状态(如果有效)。您需要将其变成单个 SQL 事务,最好具有适当的锁定。

我的建议是使用存储过程,它将返回下一个可用订单并同时更新状态。

或者,使用

ExecuteUpdate
执行更新,然后选择记录,如下所示。

var key = Guid.NewGuid();
var rowsUpdated = _dbContext.Orders
      .Where(o => o.Status == OrderStatus.Available && o.Username == null)
      .OrderByDescending(o => o.Type)
      .ThenBy(o => o.DateOfService)
      .Take(1) // untested, but I don't see why it shouldn't work since it's an IQueryable
      .ExecuteUpdate(setters => setters
          .SetProperty(o => o.Username, request.Username)
          .SetProperty(o => o.Status, OrderStatus.Available)
          .SetProperty(o => o.SomeKey, key.toString()) // need to add SomeKey to model
       );

if (rowsUpdated == 1){
    var nextOrder = _dbContext.Orders
        .FirstOrDefault(o => o.SomeKey == key.toString());
}

相关文档:

https://learn.microsoft.com/en-us/ef/core/ saving/execute-insert-update-delete#executeupdate

https://learn.microsoft.com/en-us/ef/core/ saving/concurrency?tabs=data-annotations#using-isolation-levels-for-concurrency-control

© www.soinside.com 2019 - 2024. All rights reserved.