当前位置:首页 > 文章列表 > 数据库 > Redis > Redis Lua 脚本怎样把检查与扣减保持为一个原子操作

Redis Lua 脚本怎样把检查与扣减保持为一个原子操作

来源:17golang原创 2026-11-14 03:36:00 0浏览 收藏

如果客户端先读取库存,再根据结果发送扣减命令,检查和扣减之间就隔着一次网络往返;两个请求可能同时读到同一个库存值,随后都判断“够用”并继续扣减。把这段逻辑放进 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 版本把库存读取和扣减放在脚本内部,客户端只发送一次调用;脚本的边界就是本次库存判断和写入。

客户端、EVAL、Lua 执行上下文、库存键、余额判断和扣减写入之间的静态关系说明图
图1:客户端请求、EVAL 与库存读写边界的结构说明图,不是 Redis 运行截图。

把键名和业务参数分别放入 KEYS 与 ARGV

EVAL 的前两个参数分别是脚本内容和键数量,后续参数先列出键名,再列出普通参数。脚本通过 KEYS 读取键名,通过 ARGV 读取扣减量、最低阈值等业务输入。不要把业务参数混进 KEYS,也不要在脚本里拼出另一个需要访问的键名。

这种区分不只是命名习惯。Redis 文档要求脚本访问的所有键都显式作为键参数提供,这对独立部署和 Redis Cluster 都重要。本文只有一个库存键,因此调用时键数量是 1;如果以后增加冻结库存键,就应该显式增加第二个 KEYS 参数,并确认相关键满足部署拓扑的约束。

EVAL 参数如何进入 KEYS 和 ARGV 并连接 GET、DECRBY 与状态码返回的结构说明图
图2:KEYS、ARGV 与脚本内部命令及状态返回的关系说明图,不是终端或控制台截图。

编写检查后扣减的 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 调用到可维护脚本

  1. 固定脚本文本:把脚本当作版本化资源,业务变化时更新脚本版本,而不是为每个请求拼出不同源码。
  2. 核对参数契约:先写清 KEYS 的数量和顺序,再写 ARGV 的类型、范围和默认规则。
  3. 限制脚本范围:脚本只做必要的数据读取和条件写入,避免把慢查询、远程调用或长循环塞进原子区。
  4. 设计可区分返回值:把业务不满足和执行异常分开,让客户端不会因为“库存不足”而重复提交同一请求。
  5. 检查多键部署约束:如果脚本访问多个键,所有键都要显式放进 KEYS,并按目标部署方式检查键的组织规则。

总结一下,Redis Lua 脚本的价值不在于把多条命令简单拼在一起,而在于把一次业务判断收缩成服务端的原子边界。用 KEYS 声明资源,用 ARGV 传入业务参数,用固定状态码表达业务结果,再把命令错误单独交给基础设施处理,检查和扣减就会更容易维护。

相关问答

为什么不能在脚本里动态拼库存键?

脚本访问的键应显式作为 KEYS 输入,这样 Redis 才能知道脚本涉及哪些键,集群场景也能据此处理键的分布。需要访问另一个键时,应修改调用契约显式传入。

库存不足时应该返回错误还是返回 0?

库存不足是预期的业务分支,用整数状态码返回通常更容易让客户端区分;参数非法或 Redis 命令失败则应作为错误处理。关键是先固定契约,不要让同一个状态在不同调用方有不同含义。

使用 Lua 后还需要幂等键吗?

需要。Lua 保证的是脚本内部读写的原子性,不能自动判断客户端是否因为超时而重复提交同一个订单。订单创建、支付或消息投递仍应按业务需要设计幂等键。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Redis EXPIRE 与 SET EX 参数同时使用时谁决定过期时间Redis EXPIRE 与 SET EX 参数同时使用时谁决定过期时间
上一篇
Redis EXPIRE 与 SET EX 参数同时使用时谁决定过期时间
Redis LCS 返回的中文匹配位置为什么不能直接当字符下标
下一篇
Redis LCS 返回的中文匹配位置为什么不能直接当字符下标
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1018次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1020次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1155次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1121次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    962次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码