高并发秒杀防超卖需分层协同:前端限流与幂等校验筛请求,Redis+Lua原子预扣减作第一道裁判,MySQL乐观锁或条件更新做最终一致性保障,并通过定时补偿、库存回滚和全链路日志兜底异常。

高并发秒杀场景下,库存扣减防超卖不是靠单一技术解决的,而是分层拦截、多级校验、原子操作与最终一致性协同作用的结果。核心思路是:把压力拦在前面,把扣减锁在关键路径,把不一致兜在最后。
前端与网关层:先筛再限
真正进入后端的请求,越少越好。这不是优化,是必要防线。
- 按钮置灰 + 防抖(3~5秒),禁用重复提交,从用户行为源头掐断无效请求
- 登录态 + 用户ID + 商品ID 组合做接口级幂等校验(如用Redis SETNX存唯一请求标识,10分钟过期)
- 网关层配置令牌桶或漏桶限流(如Spring Cloud Gateway集成Sentinel),按商品维度限流,例如单商品每秒最多500次请求
- 未支付订单期间,该用户对该商品的下单入口直接禁用(前端隐藏+后端校验)
缓存层:预扣减 + 原子性兜底
数据库不能直接扛住万级并发读写,Redis 是第一道“库存裁判”。
- 秒杀开始前,将商品库存同步到 Redis(如 key: stock:1001,value: 100)
- 扣减使用 DECR 或 Lua 脚本(保证读-判-减三步原子):
if redis.call("get", KEYS[1]) >= tonumber(ARGV[1]) then
return redis.call("decrby", KEYS[1], ARGV[1])
else
return -1
end - 返回 ≤0 时立即拒绝,不进后续流程;返回成功值才走下一步
- 注意:Redis 预减成功 ≠ 最终成交,只是获得“资格”,后续失败需回滚(见下文)
服务与数据库层:最终落库 + 强一致性保障
缓存只是快车道,数据库才是权威账本。这里必须守住两个底线:不超卖、不丢单。
立即学习“Java免费学习笔记(深入)”;
- 数据库库存字段加 乐观锁:增加 version 字段或用 WHERE stock >= ? AND version = ? 更新,更新失败则重试或降级提示
- 或采用 SELECT FOR UPDATE(悲观锁),但必须确保查询和更新在同一个事务内,且索引覆盖到位(避免锁表)
- 推荐组合:先用 Redis 预减筛选,再用数据库 UPDATE ... WHERE stock >= ? 做最终校验(SQL 自带原子性),失败则记录日志并触发库存补偿
- 订单创建与库存扣减必须在同一本地事务中(@Transactional),或通过可靠消息(如RabbitMQ confirm + 死信队列)保证最终一致
异常与兜底:超时、失败、冻结库存回收
真实系统里,网络超时、服务降级、用户放弃,都会让库存“卡住”。这部分不做,迟早出问题。
- 下单成功但支付超时(如15分钟未支付),自动释放 Redis 预占库存,并异步回调数据库恢复可用库存
- 数据库扣减成功但订单创建失败?靠定时任务扫描“有库存扣减无订单”的脏数据,做对账与回滚
- 所有库存变更操作记录明细日志(含 traceId、用户ID、商品ID、变更量、时间戳),便于事后追溯与人工干预
- 设置全局库存预警(如剩余≤5%时短信告警),配合人工运营快速响应


















