You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多实例多线程服务器场景下如何解决库存扣减竞态条件问题

库存并发扣减场景最优解决方案(.NET EF Core + PostgreSQL)

最优方案:原子更新 + 数据库CHECK约束

这个方案兼顾性能、可维护性和可靠性,是该场景下的首选:

实现步骤

  1. 添加数据库兜底约束
    首先给Quantity列添加非负校验约束,从数据库层面彻底避免库存为负的可能,SQL如下:
ALTER TABLE products ADD CONSTRAINT check_quantity_non_negative CHECK (quantity >= 0);

EF Core 也可以通过Fluent API配置该约束,避免手动写SQL:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>()
        .HasCheckConstraint("check_quantity_non_negative", "quantity >= 0");
}
  1. 执行原子扣减操作
    不要先查询库存再更新,直接用EF Core的ExecuteUpdateAsync执行单条原子更新SQL,整个操作在数据库层面是原子的,天然避免并发问题:
public async Task<bool> DeductStockAsync(int productId, int requestedQuantity)
{
    var affectedRows = await _dbContext.Products
        .Where(p => p.Id == productId && p.Quantity >= requestedQuantity)
        .ExecuteUpdateAsync(p => p.SetProperty(x => x.Quantity, x => x.Quantity - requestedQuantity));
    
    // 影响行数为0代表库存不足或商品不存在
    return affectedRows > 0;
}

方案优势

  • 性能最优:仅一次数据库交互,没有额外的查询和事务开销
  • 可维护性高:逻辑都在业务代码中,方便调试、版本管理
  • 可靠性强:数据库CHECK约束做兜底,不会出现负库存

你提到的两个方案的适用性分析

1. 存储过程

不推荐作为首选方案,仅适合有极复杂多表联动逻辑的场景:

  • 缺点:逻辑耦合在数据库层,代码调试、版本管理难度大,可维护性远低于代码实现的原子更新
  • 性能和原子更新方案相当,但维护成本高很多

2. SELECT ... FOR UPDATE

可以解决问题,但不是最优:

  • 实现需要显式开启事务,多一次数据库查询开销,事务持锁时间更长,高并发场景下性能低于原子更新
  • EF Core 实现示例:
using var transaction = await _dbContext.Database.BeginTransactionAsync();
try
{
    // 查询时加行锁,其他事务必须等当前事务提交后才能查询该条数据
    var product = await _dbContext.Products
        .FromSqlInterpolated($"SELECT * FROM products WHERE id = {productId} FOR UPDATE")
        .FirstOrDefaultAsync();
    
    if (product == null || product.Quantity < requestedQuantity)
    {
        await transaction.RollbackAsync();
        return false;
    }

    product.Quantity -= requestedQuantity;
    await _dbContext.SaveChangesAsync();
    await transaction.CommitAsync();
    return true;
}
catch
{
    await transaction.RollbackAsync();
    throw;
}

其他可参考的实现思路

乐观锁

适合并发冲突概率较低的场景:
给Product实体添加并发令牌(可以是版本号列,或者直接给Quantity列加[ConcurrencyCheck]标记),更新时如果数据被其他事务修改,EF Core会抛出DbUpdateConcurrencyException,业务代码捕获后重试即可。
优点是无需加锁,性能更高;缺点是冲突频繁时重试成本高,不如悲观方案稳定。

分布式锁

如果扣减库存的同时需要执行其他关联业务操作(比如生成订单、记录流水、推送消息),可以用Redis实现细粒度分布式锁,对对应商品ID加锁,锁粒度小,性能也不错。
缺点是需要额外维护Redis组件,还要处理锁超时、锁续期等问题,复杂度高于数据库层面的方案。

内容的提问来源于stack exchange,提问作者gzsun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 01:15:08