Web API乐观锁和悲观锁

4 篇文章 1 订阅
4 篇文章 1 订阅

在 Web API 中,乐观锁(Optimistic Locking)和悲观锁(Pessimistic Locking)是两种常见的并发控制机制。它们的目的都是在多个用户同时访问和修改相同资源时,确保数据的一致性和完整性。

乐观锁

乐观锁的思想是假设并发访问的操作不会造成冲突,因此在读取和修改资源时不加锁,而是通过版本号或时间戳等机制来检测并发冲突。当两个或多个用户同时修改同一资源时,系统会比对版本号或时间戳,并根据结果判断是否发生了冲突。如果发生了冲突,可以选择回滚事务或采取其他处理方式。
在 Web API 中实现乐观锁通常涉及以下步骤:

  • 在数据模型中增加一个版本号字段或时间戳字段。
  • 在更新操作中比对客户端提交的版本号或时间戳与数据库中的值是否一致。
  • 如果一致,则执行更新操作并递增版本号或更新时间戳。
  • 如果不一致,则说明发生了并发冲突,根据业务需求进行相应的处理,例如返回冲突错误信息或重新尝试操作。
// 定义数据模型,包含版本号字段
public class Order
{
    public int Id { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
    public int Version { get; set; }
}
// 定义更新操作接口
[HttpPut("{id}")]
public async Task<IActionResult> UpdateOrder(int id, [FromBody] Order order)
{
    // 查询数据库中的订单记录
    var existingOrder = await _dbContext.Orders.FindAsync(id);

    // 检查版本号是否一致
    if (existingOrder.Version != order.Version)
    {
        return Conflict("The order has been updated by another user. Please refresh and try again.");
    }

    // 更新订单记录,并递增版本号
    existingOrder.ProductName = order.ProductName;
    existingOrder.Quantity = order.Quantity;
    existingOrder.Version++;

    // 提交事务
    try
    {
        await _dbContext.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException ex)
    {
        // 处理并发冲突异常
        return Conflict("The order has been updated by another user. Please refresh and try again.");
    }

    // 返回更新后的订单记录
    return Ok(existingOrder);
}

我们通过检查客户端提交的订单记录的版本号与数据库中的版本号是否一致来判断是否发生了并发冲突。如果版本号不一致,则返回冲突错误信息;否则,更新数据库中的订单记录,并递增版本号,最后返回更新后的订单记录。

需要注意的是,如果多个用户同时修改同一条订单记录,只有一个用户能够成功地提交更新操作,其他用户需要重新获取最新版本的订单记录并重新尝试更新。这种机制可以有效地避免并发冲突,确保数据的一致性和完整性。

悲观锁

悲观锁的思想是假设并发访问的操作会造成冲突,因此在读取和修改资源时会加锁,阻止其他用户同时修改。悲观锁通常使用数据库的锁机制实现,如行级锁或表级锁。
在 Web API 中实现悲观锁通常需要对数据源进行加锁操作,以确保资源的独占性。具体实现方式包括:

  • 使用关系型数据库提供的事务和锁机制,在读取和修改资源时加锁,并释放锁。
  • 使用分布式锁机制,如 Redis 分布式锁,在操作期间将资源锁定,并在操作完成后释放锁。
  • 需要注意的是,悲观锁可能会带来性能开销,并且在高并发情况下可能导致资源争用和死锁等问题。因此,在选择使用乐观锁还是悲观锁时,需要根据具体场景和需求进行权衡和选择。
  • 无论是乐观锁还是悲观锁,在实际应用中都需要综合考虑数据一致性、并发性能和系统复杂度等因素,选择适合的并发控制策略。
// 定义数据库上下文
public class ApplicationDbContext : DbContext
{
    public DbSet<Order> Orders { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 在数据库中创建唯一索引,用于加锁
        modelBuilder.Entity<Order>()
            .HasIndex(o => o.Id)
            .IsUnique()
            .HasFilter(null);
    }
}
// 定义数据模型
public class Order
{
    public int Id { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
}
// 定义更新操作接口
[HttpPut("{id}")]
public async Task<IActionResult> UpdateOrder(int id, [FromBody] Order order)
{
    // 加锁
    using (var dbContextTransaction = await _dbContext.Database.BeginTransactionAsync())
    {
        try
        {
            // 查询数据库中的订单记录并加锁
            var existingOrder = await _dbContext.Orders.FirstOrDefaultAsync(o => o.Id == id, cancellationToken: dbContextTransaction.GetDbTransaction().Connection);
            if (existingOrder == null)
            {
                return NotFound();
            }

            // 更新订单记录
            existingOrder.ProductName = order.ProductName;
            existingOrder.Quantity = order.Quantity;

            // 提交事务
            await _dbContext.SaveChangesAsync();
            await dbContextTransaction.CommitAsync();
        }
        catch (Exception)
        {
            // 处理异常
            await dbContextTransaction.RollbackAsync();
            throw;
        }
    }

    // 返回更新后的订单记录
    return Ok(order);
}

我们通过使用数据库事务和行级锁机制实现悲观锁。在更新操作开始时,我们通过查询数据库并加锁获取订单记录。然后,我们对订单记录进行更新,最后提交事务。如果在更新过程中发生异常,将回滚事务并处理异常。

注意

悲观锁使用了数据库的锁机制,确保了资源的独占性,但也可能带来性能开销和并发性能问题。因此,在使用悲观锁时需要谨慎考虑,并根据具体情况进行权衡和选择。

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oh-caiii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值