高并发订单服务库存超卖解决方案
问题分析
库存超卖是指在并发环境下,多个请求同时读取同一商品的库存,都判断有货后进行扣减,导致实际销售数量超过库存数量的现象。主要原因包括:
- 并发读取库存时数据不一致
- 库存扣减操作非原子性
- 缺乏有效的并发控制机制
解决方案设计
1. 数据库层解决方案
1.1 乐观锁实现
UPDATE inventory
SET stock = stock - #{quantity}, version = version + 1
WHERE product_id = #{productId}
AND version = #{version}
AND stock >= #{quantity}
技术选型依据:
- 实现简单,基于现有数据库能力
- 适合冲突较少的场景
- 无锁设计,性能较好
1.2 悲观锁实现
SELECT * FROM inventory WHERE product_id = #{productId} FOR UPDATE;-- 业务逻辑处理UPDATE inventory SET stock = stock - #{quantity} WHERE product_id = #{productId};
技术选型依据:
- 强一致性保证
- 适合冲突较多的场景
- 注意锁粒度控制,避免性能问题
2. 应用层解决方案
2.1 分布式锁
// 使用Redisson实现
RLock lock = redissonClient.getLock("inventory_lock:" + productId);
try {if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {// 处理库存扣减逻辑}
} finally {lock.unlock();
}
技术选型依据:
- Redis高性能,适合分布式环境
- 需要处理锁续期、死锁等问题
- Redisson提供了完善的分布式锁实现
2.2 令牌桶限流
// 使用Guava RateLimiter
RateLimiter limiter = RateLimiter.create(1000); // 每秒1000个请求
if (limiter.tryAcquire()) {// 处理请求
} else {// 限流处理
}
技术选型依据:
- 控制请求速率,减轻系统压力
- 简单易实现
- 可作为辅助手段配合其他方案
3. 架构层解决方案
3.1 库存预扣减+异步确认
1. 预扣减库存(Redis原子操作)
2. 创建订单(状态为待确认)
3. 异步任务确认库存并更新订单状态
4. 超时未确认则回滚
技术选型依据:
- Redis原子操作保证高性能
- 异步化提高系统吞吐量
- 需要处理最终一致性问题
3.2 分片库存设计
// 将库存拆分为多个分片
int shard = productId.hashCode() % SHARD_NUM;
String key = "inventory:" + productId + ":" + shard;
redisTemplate.opsForValue().decrement(key, quantity);
技术选型依据:
- 提高并发处理能力
- 需要合理设计分片策略
- 适合SKU较多的场景
技术选型对比
方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
---|---|---|---|---|
乐观锁 | 最终 | 高 | 低 | 冲突较少 |
悲观锁 | 强 | 中 | 中 | 冲突较多 |
分布式锁 | 强 | 中 | 高 | 分布式系统 |
令牌桶 | - | 高 | 低 | 限流场景 |
预扣减 | 最终 | 高 | 高 | 高并发 |
分片 | 强 | 高 | 中 | SKU多 |
推荐综合方案
对于大型电商系统,推荐采用分层解决方案:
- 接入层:Nginx限流 + 令牌桶限流
- 应用层:分布式锁(Redisson) + 库存预扣减
- 数据层:分片库存 + 乐观锁
- 补偿机制:定时任务检查库存一致性
实施注意事项
- 监控库存扣减失败率,及时调整策略
- 设计合理的库存回滚机制
- 压测验证方案有效性
- 考虑热点商品特殊处理(如秒杀商品)
- 记录详细日志便于问题排查
扩展思考
- 可考虑引入消息队列(Kafka/RocketMQ)实现库存扣减异步化
- 对于秒杀场景,可采用本地库存+分布式协调的方案
- 长期可考虑引入库存服务,专门处理库存相关逻辑