news 2026/4/16 14:04:51

Redis+Lua原子扣减库存|Redisson分布式锁|性能对比|看门狗续期|电商秒杀|避免死锁

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis+Lua原子扣减库存|Redisson分布式锁|性能对比|看门狗续期|电商秒杀|避免死锁

用 Lua 脚本实现原子性库存扣减

一 设计思路与原子性原理

  • 将“库存校验 + 扣减 + 幂等”全部下沉到 Redis 的一条Lua 脚本中执行,Redis 以单线程顺序执行脚本,期间不会被其他命令插入,天然具备原子性,无需额外的分布式锁。
  • 典型流程:
    1. 校验业务规则(如一人一单);2) 校验库存是否充足;3) 执行扣减;4) 写业务标记(如已购集合);5) 返回结果码。
  • 该模式在高并发秒杀中已被广泛验证,可显著降低锁竞争与回滚成本,吞吐显著高于基于锁的方案。

二 单品扣减脚本与 Java 调用

  • 脚本功能:校验用户是否重复购买,校验库存是否充足,扣减库存并记录用户,返回剩余库存或错误码
  • 返回码约定:-2 重复下单-1 库存不足≥0 扣减后剩余库存

Lua 脚本 seckill.lua

-- KEYS[1] 库存key,KEYS[2] 已购用户集合key-- ARGV[1] 购买数量,ARGV[2] 用户IDlocalstockKey=KEYS[1]localboughtKey=KEYS[2]localquantity=tonumber(ARGV[1])localuserId=ARGV[2]-- 1) 重复下单校验ifredis.call('SISMEMBER',boughtKey,userId)==1thenreturn-2end-- 2) 库存校验localremain=tonumber(redis.call('GET',stockKey))ifnotremainorremain<quantitythenreturn-1end-- 3) 扣减库存 & 记录用户redis.call('DECRBY',stockKey,quantity)redis.call('SADD',boughtKey,userId)returnremain-quantity

Java 调用(Spring Boot + StringRedisTemplate)

@ServicepublicclassStockService{@AutowiredprivateStringRedisTemplateredisTemplate;privatestaticfinalStringLUA_SHA="stock:deduct:lua";// 预加载后的 SHA// 预加载脚本(应用启动或首次调用时)@PostConstructpublicvoidinit(){Stringscript=""" local stockKey = KEYS[1] local boughtKey = KEYS[2] local quantity = tonumber(ARGV[1]) local userId = ARGV[2] if redis.call('SISMEMBER', boughtKey, userId) == 1 then return -2 end local remain = tonumber(redis.call('GET', stockKey)) if not remain or remain < quantity then return -1 end redis.call('DECRBY', stockKey, quantity) redis.call('SADD', boughtKey, userId) return remain - quantity """;LUA_SHA=redisTemplate.execute((RedisCallback<String>)conn->conn.scriptLoad(script.getBytes(StandardCharsets.UTF_8)));}// 扣减库存publicLongdeduct(Stringsku,intquantity,LonguserId){List<String>keys=Arrays.asList("stock:"+sku,"bought:"+sku);Objectres=redisTemplate.execute((RedisConnectionconn)->conn.evalSha(LUA_SHA,ReturnType.INTEGER,keys.size(),keys.stream().map(String::getBytes).toArray(),String.valueOf(quantity).getBytes(),String.valueOf(userId).getBytes()));return(Long)res;// >=0 成功剩余;-1 售罄;-2 重复}}
  • 要点:使用SCRIPT LOAD + EVALSHA减少网络传输与解析开销;脚本内完成全部判断与写入,避免竞态与回滚。

三 多商品原子扣减脚本

  • 场景:一次下单涉及多个 SKU,需要“全成功或全失败”的原子性。
  • 思路:先批量MGET获取各 SKU 库存,任一不足则立即返回“哪些不足”;全部充足再逐个DECRBY

Lua 脚本 multi_deduct.lua

-- KEYS: 库存key 列表;ARGV: 购买数量列表(与 KEYS 一一对应)localstocks=redis.call('MGET',unpack(KEYS))localargs={unpack(ARGV)}-- 1) 任一不足,收集不足项并返回localinsufficient={}fori=1,#stocksdolocalremain=tonumber(stocks[i])ifnotremainorremain<tonumber(args[i])thentable.insert(insufficient,KEYS[i]..'='..tostring(remainor0))endendif#insufficient>0thenreturninsufficientend-- 2) 全部充足,逐个扣减fori=1,#stocksdoredis.call('DECRBY',KEYS[i],tonumber(args[i]))endreturn{}-- 空表表示全部成功

Java 调用

publicList<String>multiDeduct(Map<String,Integer>skuQtyMap){List<String>keys=newArrayList<>(skuQtyMap.keySet());List<String>qtys=keys.stream().map(k->String.valueOf(skuQtyMap.get(k))).toList();Objectres=redisTemplate.execute((RedisConnectionconn)->conn.evalSha(LUA_SHA_MULTI,ReturnType.MULTI,keys.size(),keys.stream().map(String::getBytes).toArray(),qtys.stream().map(String::getBytes).toArray()));// 返回空列表表示成功;非空为“库存不足清单”return(List<String>)res;}
  • 说明:脚本保证了“检查 + 扣减”的原子性;若需要“事务性回滚”(某一步失败整体回滚),可在扣减前先写入预留标记并在失败时补偿,或采用“两阶段提交 + 对账补偿”。

🔥 关注公众号【云技纵横】,开始更新redis缓存进阶,包含手写缓存注解,缓存雪崩等内容哟!

四 生产级注意事项与兜底

  • 幂等与防超卖:对“一人一单”使用SET/Redis 原子操作记录已购;对“不存在的 SKU”直接返回失败,避免无效请求进入脚本。
  • 集群与一致性:在Redis 主从异步复制下,极端故障切换可能丢写;建议开启AOF(appendfsync everysec)、合理副本数,或使用Redis-Raft等强一致方案;核心链路可结合Canal + MQ异步落库与定时对账补偿,保障最终一致性。
  • 性能与稳定性:优先使用EVALSHA;对热点 SKU 可结合本地缓存 + Redis多级缓存;活动高峰配置降级/静态兜底限流,避免雪崩。
  • 监控与压测:关注命中率、RT、QPS、脚本错误率、主从延迟;压测覆盖“单品/多品充足/不足并发冲突”场景,验证返回码与补偿链路。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 14:04:41

YOLO模型训练硬件选型建议:GPU型号对比与推荐

YOLO模型训练硬件选型建议&#xff1a;GPU型号对比与推荐 在工业质检产线高速运转的今天&#xff0c;一个摄像头每秒要处理上百帧图像——如果目标检测模型训练得慢、部署得卡&#xff0c;整个自动化流程就会“堵车”。YOLO作为实时检测的标杆算法&#xff0c;早已不是实验室里…

作者头像 李华
网站建设 2026/4/10 9:18:15

YOLO与Flask/Django集成:构建Web端检测服务的路径

YOLO与Flask/Django集成&#xff1a;构建Web端检测服务的路径 在智能制造车间里&#xff0c;一台摄像头正实时监控传送带上的零件流动。突然&#xff0c;系统自动弹出告警&#xff1a;“异物检出”——一个不该出现的金属碎片被精准识别并框出。这背后没有复杂的客户端软件&am…

作者头像 李华
网站建设 2026/4/11 7:28:45

YOLO与ONNX格式转换指南:打通不同框架的壁垒

YOLO与ONNX格式转换指南&#xff1a;打通不同框架的壁垒 在工业质检流水线上&#xff0c;一个摄像头每秒捕捉上百帧图像&#xff0c;系统必须在毫秒级内判断是否存在缺陷产品。这种对实时性近乎苛刻的要求&#xff0c;正是目标检测技术大显身手的舞台。而在这类场景中&#xf…

作者头像 李华
网站建设 2026/4/9 0:01:27

YOLO在零售门店的应用:商品识别与客流统计双丰收

YOLO在零售门店的应用&#xff1a;商品识别与客流统计双丰收 在实体零售门店仍占消费主流的今天&#xff0c;如何用最低成本实现“看得见、管得清、反应快”的运营闭环&#xff0c;是每一家连锁商超和便利店都在思考的问题。人工盘点效率低、缺货难以及时发现、顾客动线无从分析…

作者头像 李华
网站建设 2026/4/16 12:06:59

2025年度全景复盘:技术成长、创作突破与生活的三重奏

在代码与文字的交织中&#xff0c;我找到了属于自己的节奏——这一年&#xff0c;我不只是写代码的程序员&#xff0c;更是用技术思考生活的创作者。 文章目录一、开篇&#xff1a;为什么需要这样一次深度复盘&#xff1f;二、技术成长与突破&#xff1a;从“会用”到“懂为什么…

作者头像 李华