Redis集群性能爆表,轻松应对超卖难题,实战经验分享
- 问答
- 2026-01-17 17:27:50
- 3
前段时间,我们电商系统遇到了一个老生常谈但又非常头疼的问题:超卖,尤其是在大促的时候,比如双十一或者618,几千人甚至上万人同时抢购几十件特价商品,库存一下子就变成负数了,明明库存只有100件,最后却卖出了120件,这给后续的发货和客服带来了巨大的麻烦,我们试过用数据库的行锁,也试过在应用层用Java的synchronized锁,但效果都不理想,数据库锁在高并发下根本扛不住,性能下降得厉害,页面卡死;应用层的锁又只能在单台服务器上起作用,我们现在都是用多台服务器做负载均衡的,锁不住所有请求。
就在我们一筹莫展的时候,团队里有同事提到了Redis,说它的单线程特性和原子操作可能能解决这个问题,我们抱着试试看的心态开始研究,最后决定采用Redis集群的方案来彻底攻克超卖难题,这里分享一些我们实实在在踩过坑、验证过的经验。
第一招:用Redis原子操作扣减库存,这是核心
我们之前最大的教训就是,检查库存和扣减库存是两个动作,在高并发下,这两个动作如果不是“原子”的(意思就是不可分割,要么一起成功,要么一起失败),就一定会出问题,可能两个请求同时检查到库存都是1,然后都去执行扣减,结果库存就变成-1了。
Redis的命令是单线程执行的,它的某些命令本身就是原子操作,非常适合这种场景,我们最终选用了DECR命令,具体做法是:
- 预热库存:在活动开始前,我们提前把商品的库存数量,比如100件,设置到Redis的一个键里,命令很简单:
SET stock:sku_id_123 100,这里sku_id_123就是商品唯一的标识。 - 扣减库存:当用户下单时,前端请求到达后端,我们不直接查数据库,而是向Redis集群发送一个
DECR stock:sku_id_123命令,这个命令非常关键,它会做两件事:先将键对应的值减1,然后返回减1之后的值,这一切是在Redis内部一步完成的,是原子的,所以绝对不会出现两个请求拿到相同值的情况。 - 判断结果:我们拿到
DECR命令返回的结果,如果结果大于等于0,说明扣减成功,用户抢到了商品,我们再去数据库里生成订单,如果结果小于0,说明库存已经扣成负数了,也就是卖完了,这时我们立刻给用户返回“库存不足”的提示,这里有个小技巧,扣成负数后,我们还会执行一个INCR命令把库存加回来,保持数据整洁,但业务上已经认为它是无效请求了。
这一招直接解决了超卖的核心技术问题,根据我们压测的数据(来源自我们内部的压测报告),单纯使用Redis原子操作扣库存,相比之前用数据库锁的方案,在高并发下的吞吐量提升了将近20倍,而且系统非常稳定。
第二招:上Redis集群,应对海量并发和保证高可用
单机的Redis虽然快,但毕竟有性能上限,而且万一这台机器宕机了,整个抢购活动就全完了,我们毫不犹豫地选择了Redis集群模式。
- 数据分片:Redis集群会把数据自动分到多个主节点上,比如我们有3个主节点,那么
stock:sku_id_123这个键会根据算法被分配到其中一个节点上。stock:sku_id_456可能会被分配到另一个节点,这样,扣减不同商品库存的请求就能被分散到不同的Redis服务器上去处理,压力自然就分散了,实现了水平扩展,这就像是一个超市开了多个收银台,而不是所有人都挤在一个台前排队。 - 高可用:每个主节点都配备了一个或多个从节点,主节点负责写操作(比如扣减库存),从节点实时同步主节点的数据,负责读操作,如果某个主节点宕机了,集群会自动从它的从节点中选举出一个新的主节点来继续提供服务,整个过程对我们是透明的,业务系统几乎无感知,这确保了服务的连续性,在大促这种关键时刻,稳定性是生命线。
第三招:实战中遇到的坑和解决办法
方案听起来很完美,但实际部署时也遇到了问题,最主要的就是网络闪断导致的“库存扣减了,但订单没生成”。
应用服务器执行了DECR命令,Redis也成功扣减了库存,但就在应用服务器准备往数据库写订单的时候,网络突然波动了一下,或者应用服务器本身卡顿了,导致写订单失败,这时,库存已经少了1,但订单却没生成,这个商品就“消失”了,相当于少卖了一件。
为了解决这个问题,我们引入了“补偿机制”,我们的做法是:
- 在扣减Redis库存之前,先往一个特定的Redis队列(List类型)里写入一条消息,内容包含用户ID、商品ID等。
- 然后执行
DECR扣减库存。 - 如果扣减成功,应用服务继续完成创建订单等后续逻辑,成功创建订单后,再从那个队列里移除这条消息。
- 我们启动一个独立的定时任务,每隔一段时间就去扫描这个队列,如果发现队列里还有消息,说明这些消息对应的库存扣减了但订单可能没成功,定时任务会取出这些消息,检查数据库里是否真的没有对应的订单,如果确实没有,就执行“回滚”操作,即调用
INCR命令把库存加回去,并记录日志告警,让运营人员后续人工介入处理。
这个补偿机制虽然增加了一些复杂性,但极大地保证了数据的最终一致性,避免了“少卖”这种商业损失。
通过“Redis原子操作”这把利剑,结合“Redis集群”提供的扩展性和高可用能力,再辅以“补偿机制”查漏补缺,我们终于让系统稳稳地扛住了大促的洪峰流量,彻底告别了超卖噩梦,这套方案现在已经成了我们核心交易链路的标配。

本文由钊智敏于2026-01-17发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/82538.html
