Redis Lua 脚本怎样把检查与扣减保持为一个原子操作
如果客户端先读取库存,再根据结果发送扣减命令,检查和扣减之间就隔着一次网络往返;两个请求可能同时读到同一个库存值,随后都判断“够用”并继续扣减。把这段逻辑放进 Redis Lua 脚本,由一次 EVAL 交给 Redis 在服务端执行,就能让读取、判断和写回处在同一个原子执行单元中。
稳定的做法是:所有脚本实际访问的键名放进KEYS,扣减量和阈值放进ARGV,脚本只返回约定好的状态码。这样业务客户端只需要处理“成功、库存不足、参数或命令错误”三类结果。
官方参考地址:https://redis.io/docs/latest/develop/programmability/eval-intro/
先划清 Lua 脚本的原子范围
Redis 官方文档说明,Lua 脚本在 Redis 服务端执行,脚本运行期间服务器活动会被阻塞,脚本产生的效果要么尚未发生,要么已经完成。这个语义适合把“读取当前值—判断—修改值”合并起来,但它不等于脚本可以无限制地承载业务流程。
以库存键 stock:book:42 为例,客户端单独发送 GET 和 DECRBY 时,两个请求会在中间插入新的请求。Lua 版本把库存读取和扣减放在脚本内部,客户端只发送一次调用;脚本的边界就是本次库存判断和写入。

把键名和业务参数分别放入 KEYS 与 ARGV
EVAL 的前两个参数分别是脚本内容和键数量,后续参数先列出键名,再列出普通参数。脚本通过 KEYS 读取键名,通过 ARGV 读取扣减量、最低阈值等业务输入。不要把业务参数混进 KEYS,也不要在脚本里拼出另一个需要访问的键名。
这种区分不只是命名习惯。Redis 文档要求脚本访问的所有键都显式作为键参数提供,这对独立部署和 Redis Cluster 都重要。本文只有一个库存键,因此调用时键数量是 1;如果以后增加冻结库存键,就应该显式增加第二个 KEYS 参数,并确认相关键满足部署拓扑的约束。

编写检查后扣减的 Lua 脚本
下面的脚本约定三个返回值:1 表示扣减成功,0 表示库存不足,-1 表示扣减量或阈值不合法。阈值默认允许为零,意味着库存扣减后不能低于零;如果业务需要保留安全库存,只需在调用时传入更大的阈值。
-- KEYS[1] 是库存键;ARGV 只承载扣减量和最低阈值
local stock_key = KEYS[1]
local amount = tonumber(ARGV[1])
local floor = tonumber(ARGV[2])
-- 参数错误直接返回业务约定的失败码,避免把无效输入当成库存不足
if amount == nil or floor == nil or amount
调用时把固定脚本作为第一个参数,把键数量写成 1,再依次传入库存键、扣减量和最低阈值:
# 第三个参数 1 表示后面只有一个 Redis 键名
redis-cli EVAL "local amount=tonumber(ARGV[1]); local floor=tonumber(ARGV[2]); if not amount or not floor or amount
实际项目中不建议让客户端每次拼接长脚本文本;可以把脚本作为固定资源,通过客户端的脚本加载与缓存能力复用它。这里的命令只用于展示参数位置,返回值由客户端按整数处理。
用 redis.call 与返回值处理边界
redis.call() 会执行指定的 Redis 命令;如果命令参数错误或命令执行触发运行时错误,错误会直接返回给发起调用的客户端。对于库存脚本,GET 得到非数字内容时,tonumber 会得到空值,示例把它按零处理;如果业务不能接受这种数据,应改成显式错误分支,而不是默默当作空库存。
业务失败和命令错误要分开处理。返回 0 代表脚本正确执行但库存条件不满足,返回 -1 代表输入不符合约定;网络错误、脚本语法错误或 Redis 命令错误则属于调用失败。若需要在脚本内接住 Redis 命令错误,可以使用 redis.pcall(),它会把错误作为返回结果交给脚本继续判断,但不要用它掩盖数据格式问题。
客户端的处理契约可以保持很小:
| 结果 | 含义 | 客户端动作 |
|---|---|---|
1 | 检查通过并已扣减 | 继续创建订单或发放额度 |
0 | 库存低于最低阈值 | 返回库存不足,不重试同一业务请求 |
-1 | 扣减量或阈值非法 | 记录参数错误,修复调用方 |
| Redis error | 脚本或命令执行失败 | 按基础设施错误处理并评估重试 |
迁移清单:从 EVAL 调用到可维护脚本
- 固定脚本文本:把脚本当作版本化资源,业务变化时更新脚本版本,而不是为每个请求拼出不同源码。
- 核对参数契约:先写清
KEYS的数量和顺序,再写ARGV的类型、范围和默认规则。 - 限制脚本范围:脚本只做必要的数据读取和条件写入,避免把慢查询、远程调用或长循环塞进原子区。
- 设计可区分返回值:把业务不满足和执行异常分开,让客户端不会因为“库存不足”而重复提交同一请求。
- 检查多键部署约束:如果脚本访问多个键,所有键都要显式放进
KEYS,并按目标部署方式检查键的组织规则。
总结一下,Redis Lua 脚本的价值不在于把多条命令简单拼在一起,而在于把一次业务判断收缩成服务端的原子边界。用 KEYS 声明资源,用 ARGV 传入业务参数,用固定状态码表达业务结果,再把命令错误单独交给基础设施处理,检查和扣减就会更容易维护。
相关问答
为什么不能在脚本里动态拼库存键?
脚本访问的键应显式作为 KEYS 输入,这样 Redis 才能知道脚本涉及哪些键,集群场景也能据此处理键的分布。需要访问另一个键时,应修改调用契约显式传入。
库存不足时应该返回错误还是返回 0?
库存不足是预期的业务分支,用整数状态码返回通常更容易让客户端区分;参数非法或 Redis 命令失败则应作为错误处理。关键是先固定契约,不要让同一个状态在不同调用方有不同含义。
使用 Lua 后还需要幂等键吗?
需要。Lua 保证的是脚本内部读写的原子性,不能自动判断客户端是否因为超时而重复提交同一个订单。订单创建、支付或消息投递仍应按业务需要设计幂等键。
Redis EXPIRE 与 SET EX 参数同时使用时谁决定过期时间
- 上一篇
- Redis EXPIRE 与 SET EX 参数同时使用时谁决定过期时间
- 下一篇
- Redis LCS 返回的中文匹配位置为什么不能直接当字符下标
-
- 数据库 · Redis | 6分钟前 |
- Redis BITFIELD 怎样存储多个小整数并读取指定范围
- 266浏览 收藏
-
- 数据库 · Redis | 8分钟前 | redis集群 · Redis Cluster MOVED ASK hash slot resharding CLUSTER SETSLOT
- Redis Cluster resharding 时怎样减少业务请求的 MOVED
- 297浏览 收藏
-
- 数据库 · Redis | 8分钟前 | redis hyperloglog 去重 PFADD PFCOUNT 基数估算
- Redis HyperLogLog 估算去重数为什么不能取回原始成员
- 155浏览 收藏
-
- 数据库 · Redis | 10分钟前 | 排行榜 · Sorted Set · redis 分页 排行榜 有序集合 ZRANGE
- Redis ZRANGE 怎样按分数和成员稳定读取排行榜
- 189浏览 收藏
-
- 数据库 · Redis | 12分钟前 | redis集群 · redis Redis Cluster CROSSSLOT Hash Tag CLUSTER KEYSLOT
- Redis CROSSSLOT 错误怎样用 key tag 重写键名
- 460浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 1018次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 1020次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1155次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1121次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 962次使用
-
- 浅析java多线程同步和数据库事务的原子性
- 2023-02-24 471浏览
-
- MySQL 事务ACID特性
- 2023-02-25 331浏览
-
- MySQL 库存怎么安全扣减?条件 UPDATE、防超卖和受影响行判断
- 2026-07-16 470浏览
-
- Redis Lua 扣库存实战:原子校验库存和订单幂等,避免超卖
- 2026-06-14 118浏览
-
- Redis Functions 部署怎么验收:FUNCTION LOAD、FCALL 与重启恢复
- 2026-08-16 443浏览

