连载中 7/15

Redis 预扣库存:Lua 原子扣减的完整实现

2026-09-05 · 369 阅读 · 0 评论 · 0 赞

为什么是 Redis

库存扣减的执行位置决定系统上限:数据库行锁让 10 万个请求排队,实际有效吞吐只有几百;Redis 单线程内存操作,简单命令十万级 QPS——同样的扣减语义,换一个执行位置,吞吐差三个数量级。库存作为预扣资格放进 Redis,数据库只在最后承接真实订单量(几百笔),压力断崖式下降。

Lua:把判断和扣减焊死在一步

直觉写法是先 GET 判断、再 DECR 扣减——两步之间就是并发窗口:一万个请求同时 GET 到"剩 1 个",然后一万个 DECR,库存变成负数,超卖。Lua 脚本在 Redis 里原子执行:整个脚本跑完才会执行下一条命令,判断与扣减之间不存在并发窗口,超卖从机制上被消灭。

-- seckill_deduct.lua
local stock = redis.call("GET", KEYS[1])
if not stock then
    return -1                                   -- 库存 key 不存在:未预热,拒绝
end
if tonumber(stock) < tonumber(ARGV[1]) then
    return 0                                    -- 库存不足:售罄
end
return redis.call("DECRBY", KEYS[1], ARGV[1])   -- 扣减并返回剩余
// 服务端调用:三种返回值对应三种处理
Long r = redis.eval(DEDUCT_LUA, "stock:sku:1001", "1");
if (r == null || r == -1) return fail(ServiceState.NOT_READY);  // 未就绪
if (r == 0)               return fail(Result.SOLD_OUT);         // 售罄
mq.send(new OrderMsg(orderId, userId, skuId));                  // 扣成功才发消息
return queuing(orderId);                                        // 返回"排队中"

预热:开抢前库存必须就位

三个预热动作缺一不可:库存装载——活动创建时把库存数 SET 进 Redis,key 带 TTL 覆盖活动全周期;资格数据——限购名单、风控黑名单提前就位;连接与脚本——Lua 脚本用 SCRIPT LOAD 预加载(EVALSHA 比 EVAL 少传脚本体),连接池预热到目标并发。开抢前一分钟做一次自检脚本:库存 key 存在且值正确、Lua 可执行、MQ 通路健康——预热不是加载完就完了,要验证

一致性:Redis 扣了,订单没成

预扣成功只代表"资格到手",后面消息可能丢、落库可能失败——Redis 与数据库是两个存储,一致性要靠闭环设计兜底:消息可靠投递(生产确认 + 持久化 + 手动 ack);落库失败回补——消费端写库失败,发回补消息把 Redis 库存加回去;对账兜底——活动后 Redis 剩余 + 已成订单 + 回补记录 = 总库存,对不上的进人工处理。一致性不是靠某个机制一劳永逸,是靠"每个失败路径都有出口"。

Redis 挂了怎么办

Redis 成为关键路径后,它的高可用就是活动的高可用:哨兵或集群部署是底线;预扣失败(连接不上)时的降级不是退回数据库硬扛——那等于回到事故现场——而是直接返回排队页并告警,宁可活动暂停一分钟,也不冒超卖的风险;恢复后先校验库存值(有对账记录才有底气恢复)。下一篇解决预扣的下一个瓶颈:单个热 Key 扛不住一万个人怎么办

503

10 年全栈工程师 · 503咖啡馆主理人

#Redis预扣#Lua脚本#原子扣减#库存预热#防超卖

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 4 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞