news 2026/4/30 18:06:07

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/5/1 6:27:12

YOLO模型输入预处理标准化:图像归一化参数设置

YOLO模型输入预处理标准化&#xff1a;图像归一化参数设置 在工业相机高速运转的流水线上&#xff0c;一张产品图像从采集到缺陷判定往往只有几十毫秒。就在这短暂的时间内&#xff0c;深度学习模型必须准确识别出微米级的划痕或色差——而这一切的起点&#xff0c;并非复杂的网…

作者头像 李华
网站建设 2026/5/1 6:27:14

YOLO模型版本迭代路线图:未来发展方向预测

YOLO模型版本迭代路线图&#xff1a;未来发展方向预测 在智能制造产线高速运转的今天&#xff0c;每毫秒都决定着良品率与产能。一台PCB检测设备若因视觉算法延迟0.1秒&#xff0c;整条流水线就可能停滞——这正是现代工业对实时目标检测提出的严苛挑战。而在这场“速度与精度”…

作者头像 李华
网站建设 2026/5/1 6:28:19

YOLO模型冷热数据分离:长期存储与即时访问的平衡

YOLO模型冷热数据分离&#xff1a;长期存储与即时访问的平衡 在智能制造工厂的视觉质检线上&#xff0c;一个看似简单的请求——“切换到上个月的YOLOv8模型进行复检”——却可能让系统卡顿数分钟。原因并不复杂&#xff1a;边缘设备本地存储早已被高频调用的主模型占满&#x…

作者头像 李华
网站建设 2026/4/30 20:59:01

YOLO模型远程调试技巧:通过SSH连接GPU服务器

YOLO模型远程调试技巧&#xff1a;通过SSH连接GPU服务器 在智能安防摄像头实时识别可疑行为、自动驾驶车辆毫秒级响应路况变化的今天&#xff0c;深度学习工程师面临的最大挑战之一&#xff0c;早已不是算法本身&#xff0c;而是——如何让这些庞大的YOLO模型真正跑起来。 本地…

作者头像 李华
网站建设 2026/5/1 6:11:22

YOLO模型特征图可视化:理解网络关注区域的方法

YOLO模型特征图可视化&#xff1a;理解网络关注区域的方法 在工业质检线上&#xff0c;一台搭载YOLO模型的摄像头正高速扫描PCB板。它每秒能处理30帧图像&#xff0c;准确识别出焊点缺陷——但工程师却发现&#xff0c;系统频繁将正常焊盘误判为异物。问题出在哪里&#xff1f;…

作者头像 李华
网站建设 2026/4/30 10:55:13

计算机毕设java药房药品销售系统的设计与实现 基于Java的药房药品销售管理系统的设计与开发 Java环境下药房药品销售信息化管理系统的实现

计算机毕设java药房药品销售系统的设计与实现28h1d9 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着互联网技术的飞速发展和人们生活方式的改变&#xff0c;传统的药房药品销…

作者头像 李华