用Redis存库存秒杀,快速又省心的扣减方案分享
- 问答
- 2026-01-14 05:43:49
- 4
(引用来源:网络技术社区常见方案及Redis官方文档思想)
说到用Redis处理秒杀库存扣减,核心目标就两个:一是快,二是准,快是为了应对海量并发请求,不能让用户一直转圈圈;准是为了防止超卖,比如1000件商品卖出去1001件,那可就出大问题了,下面就直接分享一个在实践中比较常用且靠谱的方案。
第一步:库存预热
秒杀开始前,我们得先把库存数量放到Redis里去,这一步叫预热,别小看这一步,它至关重要,我们不能在秒杀开始的那一刻才去数据库里查库存,那样数据库瞬间就垮了,我们会使用Redis的string类型或者hash类型来存储,简单起见,用string就行。
我们有个秒杀活动,商品ID是seckill_item_123,总库存是1000件,那么在活动开始前,我们就通过一个后台程序执行一条命令:
SET seckill_item_123 1000
这样,库存数据就已经在内存里准备好了,等待秒杀开始。
第二步:关键中的关键——原子性扣减
用户点击“立即抢购”按钮,后台会收到一个请求,最危险的环节就在这里,如果很多个请求同时来查同一个库存,发现都大于0,然后都去执行扣减,超卖就发生了,扣减库存这个动作必须是“原子性”的,简单说,检查”和“扣减”必须是一个不可分割的整体操作,中间不能被打断。
Redis正好提供了这样的原子操作命令,我们绝对不能先用GET命令查询库存,再判断,再用SET命令去扣减,这套流程在并发下100%会超卖。

正确的做法是使用DECR或DECRBY命令。
DECR key:将键存储的数字值减一。DECRBY key decrement:将键存储的数字值减去指定的整数。
这两个命令是原子的,Redis会保证在执行这个命令时,不会被其他命令插入,我们的扣减逻辑就变得非常简单直接:
- 直接对
seckill_item_123执行DECR命令。 - 看命令的返回值。
- 如果返回值大于等于0,说明扣减成功,用户抢到了商品。
- 如果返回值小于0(比如是-1),说明库存已经不足,扣减失败,用户没抢到。
这里有个细节,为什么是判断大于等于0?因为DECR命令会在值不存在时当作0处理,然后减成-1,但我们预热时已经设置了库存,所以正常情况下,返回值等于0时,意味着抢到了最后一个商品,返回值是-1时,意味着库存已经是0,你再来抢,我就给你减成了-1,这就是没抢到。
伪代码逻辑看起来是这样的:
Long result = redis.decr("seckill_item_123");
if (result >= 0) {
// 恭喜你,抢购成功!
// 接下来可以进入下一步,比如生成订单等。
} else {
// 不好意思,库存不足了。
// 为了保持库存数据的准确性,我们可以把刚才减去的加回来,因为这个人没抢到。
redis.incr("seckill_item_123");
}
最后那个INCR(加一)的操作是可选的,但建议加上,这样seckill_item_123这个Key的值最终会稳定在0,而不会变成负数,便于我们后续查看真实的剩余库存(一直是0)。

第三步:后续流程处理
用户抢购成功,库存也扣减了,但事情还没完,秒杀系统不能直接在Redis里完成所有操作,比如订单的创建、支付等复杂逻辑还是得交给数据库,通常的成功流程是:
- Redis原子扣减库存成功。
- 将“用户ID”和“商品ID”等信息封装成一个消息,放入一个消息队列(比如RabbitMQ、Kafka)中,这一步也非常快,目的是快速响应用户界面,告诉用户“抢购成功,正在排队生成订单”。
- 后台有专门的订单服务从消息队列里取出消息,慢慢地、稳定地去数据库里创建订单、扣减数据库的库存等。
- 最终通过通知告诉用户订单详情。
这样做的好处是“削峰填谷”,把一瞬间对数据库的巨大压力,分摊成一段时间内平稳的压力,保护了数据库。
第四步:一些省心的小技巧
- 设置过期时间:秒杀活动结束后,记得给库存的Key设置一个过期时间,比如一天后自动删除,避免无用数据长期占用内存,命令是
EXPIRE key seconds。 - 库存回滚:如果用户抢购成功但最后没有付款,超时后我们需要把库存加回去,这时可以监听订单超时事件,然后对Redis库存执行一个
INCR命令,注意,这可能会造成少量库存多于初始值(因为可能有人未支付但库存已回滚),属于业务可接受范围,通常称为“少卖”比“超卖”好。 - 限流与验证:光有库存扣减还不够,前端和后端都要做限流和验证码等手段,防止恶意脚本刷单,把大部分请求在进入核心扣减逻辑前就拦截掉。
总结一下
这个方案的核心思路就是:预热库存到Redis + 使用原子命令(DECR)进行扣减判断 + 消息队列异步处理订单,它之所以快速,是因为所有关键操作都在内存中完成;它之所以省心,是因为利用了Redis原子命令的特性,从根源上避免了复杂的锁机制,大大降低了超卖的风险,代码写起来也清晰简单,一个完整的秒杀系统还涉及网关限流、缓存预热、防刷、降级等多方面,但库存扣减这个核心环节,用上述方案是经得起实践考验的。
本文由寇乐童于2026-01-14发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/80374.html
