乐观锁通过version字段校验防止超卖,Redis+Lua实现原子扣减,分布式锁兜底,库存分段设计提升并发吞吐。

用数据库乐观锁防止超卖
库存扣减最常见问题是多个请求同时读到相同剩余量,都判断“够用”后执行更新,结果实际扣多了。乐观锁是轻量级方案:在商品表加red">version字段,更新时带上版本号校验。
- SQL写法:UPDATE item SET stock = stock - 1, version = version + 1 WHERE id = ? AND stock >= 1 AND version = ?
- Java中用MyBatis执行后检查update影响行数是否为1;为0说明被其他线程抢先修改,需重试或抛异常
- 适合读多写少、冲突不频繁的场景,避免数据库行锁阻塞,吞吐较高
Redis+Lua脚本实现原子扣减
当库存变化频繁、并发极高(如秒杀),单靠数据库压力大。把库存放到Redis里,用Lua脚本保证“读-判-减”三步不可分割。
- 脚本示例:if redis.call("GET", KEYS[1]) >= tonumber(ARGV[1]) then redis.call("DECRBY", KEYS[1], ARGV[1]); return 1 else return 0 end
- Java调用:redisTemplate.execute(script, Collections.singletonList("item:1001"), "1")
- 注意:需配合定时任务或MQ,将Redis扣减结果异步落库,保证最终一致性
分布式锁兜底防极端竞争
乐观锁和Redis脚本能覆盖大部分情况,但遇到网络延迟、脚本执行失败等边界问题,仍可能漏控。此时用Redis分布式锁做二次保障。
- 推荐使用Redisson的RLock,支持自动续期、可重入、锁失效保护
- 锁粒度建议按商品ID划分,避免全局锁拖慢整体性能
- 务必设置合理超时时间(如3~5秒),并确保业务逻辑在锁内快速完成,防止死锁或长等待
库存预热与分段设计提升吞吐
纯靠锁或脚本还不够,架构上可进一步优化:把一个商品库存拆成N个逻辑子库存(如按用户ID哈希取模),各子库存独立扣减,互不影响。
立即学习“Java免费学习笔记(深入)”;










