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

NestJS中并发订单与库存更新的安全处理方案咨询

解决NestJS中订单并发与库存原子性更新的问题

你的代码核心问题在于库存检查与更新是分离的两个操作,中间存在时间窗口,并发请求会导致多个请求同时通过库存检查,最终出现超卖或数据不一致。下面给出几种落地性强的解决方案,确保库存操作的原子性:


1. 数据库事务 + 乐观锁(推荐中等并发场景)

通过版本号控制,在事务内完成所有操作,避免并发冲突。

实现步骤

  • 给Product实体添加版本字段:
    import { Version } from 'typeorm';
    
    @Entity()
    export class Product {
      // ...其他字段
      @Column()
      unitsOnStock: number;
      
      @Version() // 乐观锁版本号,TypeORM自动管理
      version: number;
    }
    
  • 改造订单创建逻辑,用事务包裹所有操作:
    async createOrder(userId: string, products: ProductForOrder[], paymentStatus: PaymentStatusEnum): Promise<OrdersRo> {
      return this.dataSource.transaction(async (manager) => {
        // 1. 验证用户(事务外也可,这里统一放事务内保证一致性)
        const user = await manager.findOne(User, { where: { id: userId } });
        if (!user) throw new HttpException("Invalid user's ID", HttpStatus.NOT_FOUND);
    
        // 2. 创建订单
        const order = manager.create(Order, { customer: user, paymentStatus });
        await manager.save(order);
    
        // 3. 批量处理商品:检查库存+更新+保存明细(原子性)
        for (const item of products) {
          // 事务内查询并启用乐观锁
          const product = await manager.findOne(Product, {
            where: { id: item.productId },
            lock: { mode: 'optimistic' },
          });
    
          if (!product) throw new HttpException("Product's ID not found", HttpStatus.NOT_FOUND);
          if (product.unitsOnStock < item.quantity) {
            throw new HttpException(`Insufficient stock for product: ${product.name}`, HttpStatus.BAD_REQUEST);
          }
    
          // 更新库存,TypeORM自动校验版本号
          product.unitsOnStock -= item.quantity;
          await manager.save(product);
    
          // 保存订单明细
          const detail = manager.create(OrderDetail, {
            product,
            order,
            quantity: item.quantity,
          });
          await manager.save(detail);
        }
    
        return this.toResponseOrder(order);
      });
    }
    

关键说明

  • 事务确保所有操作要么全成功,要么全回滚,避免部分更新导致的数据不一致。
  • 乐观锁通过版本号拦截并发更新:如果两个请求同时修改同一商品,第二个请求的save会抛出OptimisticLockingFailedError,此时可以捕获错误提示用户重试。

2. 悲观锁(行级锁,适合高并发场景)

直接在查询时锁定商品行,直到事务结束,彻底阻止并发修改。

修改查询逻辑

将乐观锁替换为悲观写锁:

const product = await manager.findOne(Product, {
  where: { id: item.productId },
  lock: { mode: 'pessimistic_write' }, // 写锁,其他事务只能读不能写
});

关键说明

  • 悲观锁会阻塞其他并发请求,直到当前事务释放锁,完全避免冲突,但要注意锁的持有时间,尽量缩短事务范围。
  • 建议按商品ID顺序处理订单中的商品,防止出现交叉锁导致死锁。

3. 数据库原子更新(极简方案)

直接通过SQL的UPDATE ... WHERE语句,在数据库层面完成库存检查与更新的原子操作,无需先查询再更新。

实现代码

async createOrder(userId: string, products: ProductForOrder[], paymentStatus: PaymentStatusEnum): Promise<OrdersRo> {
  return this.dataSource.transaction(async (manager) => {
    const user = await manager.findOne(User, { where: { id: userId } });
    if (!user) throw new HttpException("Invalid user's ID", HttpStatus.NOT_FOUND);

    const order = manager.create(Order, { customer: user, paymentStatus });
    await manager.save(order);

    for (const item of products) {
      // 原子更新:只有库存足够时才会执行扣减
      const updateResult = await manager.update(
        Product,
        {
          id: item.productId,
          unitsOnStock: MoreThanOrEqual(item.quantity), // 库存足够的条件
        },
        { unitsOnStock: () => `unitsOnStock - ${item.quantity}` } // 原子扣减
      );

      if (updateResult.affected === 0) {
        throw new HttpException(`Insufficient stock for product ID: ${item.productId}`, HttpStatus.BAD_REQUEST);
      }

      // 查询商品信息用于生成订单明细
      const product = await manager.findOne(Product, { where: { id: item.productId } });
      const detail = manager.create(OrderDetail, { product, order, quantity: item.quantity });
      await manager.save(detail);
    }

    return this.toResponseOrder(order);
  });
}

关键说明

  • 这条UPDATE语句是数据库级别的原子操作,保证只有当库存满足条件时才会扣减,从根源避免超卖。
  • 同样需要事务包裹,确保订单创建与库存更新的一致性。

额外优化建议

  • 缩小事务范围:把用户验证等不需要锁的操作移到事务外,减少锁的持有时间。
  • 重试机制:针对乐观锁失败的情况,可实现自动重试(如最多3次),提升用户体验。
  • 库存预扣模式:如果订单需要支付确认,可先预扣库存,超时未支付则自动释放库存,适合电商场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 11:22:48