在代购、跨境电商等高并发下单场景中,库存超卖是最常见且代价极高的问题。多服务实例、多线程并发下单时,传统本地锁失效,极易导致库存校验与扣减出现竞态条件,最终出现 “无货可发、订单积压” 的故障。

本文围绕代购系统库存预占核心流程,讲解如何通过分布式锁实现安全库存预占,从原理、流程、代码实现到生产级优化,完整落地防超卖方案。


一、为什么代购系统必须做库存预占

代购业务有三大特性,决定库存必须 “先预占、后扣减”:

  1. 采购链路长:海外货源、清关、物流周期长,库存无法实时补货
  2. 并发集中:限时折扣、爆款补货瞬间流量高,并发冲突剧烈
  3. 支付异步:用户下单后需等待支付,未支付订单需释放库存

库存预占核心目标:下单时锁定库存,支付成功正式扣减,超时未支付自动释放,从根源杜绝超卖。


二、分布式锁解决超卖的核心原理

分布式锁的作用是跨服务、跨节点互斥,保证同一时间只有一个请求能操作同一商品库存。

核心流程:一锁二判三更新

  1. 加锁:对商品 ID 加分布式锁,阻塞并发请求
  2. 校验:查询真实可用库存,判断是否足够预占
  3. 预占:库存充足则写入预占记录,扣减可用库存
  4. 释放:业务完成释放锁,异常超时自动释放锁

满足三大特性:互斥性、防死锁、防误删,是防超卖的基础保障。


三、技术选型:Redis+Redisson 生产级方案

代购系统推荐 Redis + Redisson 实现分布式锁,理由:

  • 性能高,内存操作响应快
  • Redisson 封装完善,自带看门狗自动续期
  • 支持可重入、公平锁、联锁,适配复杂业务
  • 过期时间自动释放,避免服务宕机死锁

备选:ZooKeeper/etcd 一致性更强,但性能更低,适合强一致低并发场景。


四、库存预占完整实现流程

1. 数据结构设计

  • 库存总表:goods_id、total_stock、occupied_stock、available_stock
  • 预占记录表:preempt_id、goods_id、order_id、user_id、status、expire_time
  • 锁 Key:lock:goods:{goods_id}

2. 核心业务流程

  1. 用户提交下单请求
  2. 生成唯一请求 ID,尝试获取 Redisson 分布式锁
  3. 加锁成功:查询可用库存
  4. 库存不足:直接返回 “库存不足”,释放锁
  5. 库存充足:新增预占记录,更新可用库存与预占库存
  6. 开启支付超时任务(如 15 分钟)
  7. 释放分布式锁,返回下单成功
  8. 支付成功:正式扣减库存,删除预占记录
  9. 支付超时:定时任务回滚库存,释放预占

五、代码实现(Java + SpringBoot + Redisson)

1. 分布式锁工具类封装

java

运行

@Resource
private RedissonClient redissonClient;

/**
 * 获取分布式锁
 * @param goodsId 商品ID
 * @param requestId 请求唯一ID
 * @return 锁对象
 */
public RLock getGoodsLock(Long goodsId, String requestId) {
    String lockKey = "lock:goods:" + goodsId;
    RLock lock = redissonClient.getLock(lockKey);
    // 看门狗自动续期,默认30秒,每10秒续期
    lock.lock(30, TimeUnit.SECONDS);
    return lock;
}

/**
 * 释放锁
 */
public void unlock(RLock lock) {
    if (lock != null && lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

2. 库存预占核心方法

java

运行

@Transactional(rollbackFor = Exception.class)
public Result<?> preemptStock(Long goodsId, Integer num, String orderId, Long userId) {
    String requestId = UUID.randomUUID().toString();
    RLock lock = null;
    try {
        // 1. 加分布式锁
        lock = getGoodsLock(goodsId, requestId);
        // 2. 查询可用库存
        GoodsStock stock = stockMapper.selectByGoodsId(goodsId);
        if (stock == null || stock.getAvailableStock() < num) {
            return Result.fail("库存不足");
        }
        // 3. 写入预占记录
        StockPreempt preempt = new StockPreempt();
        preempt.setGoodsId(goodsId);
        preempt.setOrderId(orderId);
        preempt.setUserId(userId);
        preempt.setNum(num);
        preempt.setStatus(0); // 0-预占中 1-已扣减 2-已释放
        preempt.setExpireTime(LocalDateTime.now().plusMinutes(15));
        preemptMapper.insert(preempt);
        // 4. 更新库存:可用库存减少,预占库存增加
        stockMapper.updateStock(goodsId, -num, num);
        return Result.success("库存预占成功");
    } finally {
        // 5. 释放锁
        unlock(lock);
    }
}

3. 支付超时自动释放(定时任务)

java

运行

@Scheduled(cron = "0 */1 * * * ?")
public void clearExpirePreempt() {
    // 查询超时预占记录
    List<StockPreempt> list = preemptMapper.selectExpirePreempt(LocalDateTime.now());
    for (StockPreempt preempt : list) {
        RLock lock = getGoodsLock(preempt.getGoodsId(), UUID.randomUUID().toString());
        try {
            // 回滚库存:可用库存增加,预占库存减少
            stockMapper.updateStock(preempt.getGoodsId(), preempt.getNum(), -preempt.getNum());
            // 更新预占状态为已释放
            preemptMapper.updateStatus(preempt.getId(), 2);
        } finally {
            unlock(lock);
        }
    }
}

六、生产级防坑要点

  1. 锁粒度精细化按商品 ID 加锁,不锁全表,提升并发能力。

  2. 请求 ID 防误删释放锁时校验请求 ID,避免释放其他线程的锁。

  3. 看门狗必开Redisson 看门狗自动续期,防止业务未执行完锁过期。

  4. 数据库事务与锁顺序先加锁,后开启事务,避免长事务持有锁导致性能下降。

  5. 库存兜底校验更新库存时 SQL 增加条件:available_stock >= #{num},防止超卖。

    sql

    UPDATE goods_stock 
    SET available_stock = available_stock - #{num}, occupied_stock = occupied_stock + #{num} 
    WHERE goods_id = #{goodsId} AND available_stock >= #{num}
    
  6. 最终一致性保障定时任务 + 对账脚本,每日核对库存、预占、订单数据,修复异常。


七、方案对比与选型建议

表格

方案优点缺点适用场景
本地锁实现简单分布式失效单体应用
Redis 分布式锁高性能、易集成主从切换有极小锁失效风险代购 / 电商高并发下单
Redis+Lua 原子脚本性能极高业务逻辑复杂难维护秒杀、极致高并发
ZooKeeper 锁强一致性性能低金融、强一致场景

代购系统首选:Redis+Redisson 分布式锁,平衡性能、可靠性与开发成本。


八、总结

代购系统防超卖的核心是库存预占 + 分布式锁,通过 Redisson 实现跨节点互斥,配合预占超时释放机制,可 100% 避免超卖。

落地关键点:

  • 按商品 ID 细粒度加锁
  • 先锁后查,SQL 兜底校验
  • 支付超时自动回滚
  • 看门狗防止锁提前释放

这套方案可直接用于生产,支撑日常下单与爆款并发,稳定可靠。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐