当前位置:首页 > 文章列表 > 数据库 > Redis > Redis Lua 脚本读取多个 key 时怎么保持原子校验

Redis Lua 脚本读取多个 key 时怎么保持原子校验

来源:17golang原创 2026-09-10 03:32:46 0浏览 收藏

库存扣减、额度占用这类逻辑,不能先在客户端 GET 两个 key,再根据结果分别 SET:两次请求之间可能已经被另一个客户端改写。更稳妥的做法是把读取、校验和修改放进一次 Redis Lua 脚本,用 EVAL 调用;脚本在 Redis 服务端原子执行。多个 key 要放在 KEYS,业务数值放在 ARGV,Redis Cluster 中还要让这些 key 落在允许的 hash slot 范围内。

要点速览
  • 一个脚本内的检查与写入具有原子性,其他 Redis 活动会等脚本结束后再处理。
  • KEYS 只放真实 key 名,ARGV 放数量、阈值、状态等普通参数。
  • 脚本应返回稳定的数字码或短结果;生产环境还要考虑脚本缓存丢失和执行时间。

官方文档:https://redis.io/docs/latest/develop/programmability/eval-intro/

把多个 key 的检查与修改收进一段脚本

先看一个“库存足够才扣减”的最小模型。库存和用户额度分别存放在两个 key 中,脚本负责读取两者,任意一项不足就返回失败,否则同时写回新值。客户端只发起一次脚本调用,不需要自己拼接检查与修改的时间窗口。

-- KEYS[1] 是库存,KEYS[2] 是用户额度;ARGV 只传本次扣减量。
local stock = tonumber(redis.call('GET', KEYS[1]) or '0')
local quota = tonumber(redis.call('GET', KEYS[2]) or '0')
local amount = tonumber(ARGV[1])

-- 输入异常和业务不足都返回稳定的数字码,便于客户端处理。
if not amount or amount 

这里的原子性不是“每条 Redis 命令都更快”,而是脚本执行期间不会被其他客户端的命令插入。失败分支没有写入,成功分支的两个 SET 也不会只完成一半。脚本应保持短小,因为 Redis 需要在脚本运行期间处理完它,过长的脚本会把延迟传给所有请求。

Redis EVAL Lua 脚本中 KEYS、ARGV、Redis Lua 引擎和多个 key 数据之间的静态关系图
图1:区分 EVAL 的脚本输入、KEYS 的 key 名和 ARGV 的业务参数,避免把动态 key 拼接在脚本内部。

KEYS 和 ARGV 不是形式要求

EVAL 的第二个参数是 key 数量。数量为 2 时,命令后紧跟的两个参数会进入 KEYS[1]KEYS[2],后面的参数才进入 ARGV。不要在 Lua 里根据用户编号拼接 key,例如 'quota:' .. ARGV[1];应由客户端先算好完整 key,再作为 KEYS 传入。

# 2 表示后面的 stock 和 quota 都是 key 名,其余 1 是普通业务参数。
redis-cli EVAL "...Lua 脚本..." 2 stock:{sku-9} quota:{user-7} 1

显式声明 key 不只是代码风格。Redis 文档要求脚本只访问作为输入传入的 key,Redis Cluster 也需要据此判断脚本涉及的 key。跨 key 操作应使用相同 hash tag,例如 stock:{sku-9}quota:{sku-9},让它们共享大括号内的路由片段;不要把不同业务对象强行塞进同一脚本,先确认它们确实需要同一原子边界。

Redis Lua 脚本把多个 key 的读取校验写入和原子执行边界连接起来的静态关系图
图2:查看读取 key、条件校验、写入命令和原子执行边界的关系,判断哪些操作必须放在同一脚本里。

返回码、脚本缓存与慢脚本边界

脚本最好返回 {1, 'ok'}{0, 'stock_not_enough'} 这样的固定结构。客户端据此区分业务拒绝、参数错误和 Redis 执行错误,而不是解析一段可能变化的提示文案。

重复执行同一参数化脚本时,可先用 SCRIPT LOAD 加载,再用 EVALSHA 按摘要调用,减少重复传输脚本内容。但脚本缓存不是持久化数据,重启、故障切换或 SCRIPT FLUSH 后可能出现 NOSCRIPT;客户端应准备回退到重新加载再执行。脚本中不要访问未声明的 key,也不要把大批量扫描塞进一次调用。

常见问题

Lua 脚本执行时其他客户端会插队吗?

不会。Redis 保证脚本原子执行,其他服务活动会等待脚本运行结束,所以脚本越长,对全局延迟的影响越明显。

ARGV 可以用来传 key 名吗?

不建议。需要被脚本访问的 key 应显式放入 KEYS;ARGV 适合数量、阈值、状态和值等普通参数。

多个 key 在 Redis Cluster 中一定能一起操作吗?

不一定。它们需要满足集群脚本的路由约束,常见做法是使用相同 hash tag;先按部署模式验证 slot 规则,再决定脚本边界。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Redis ZINCRBY 计数出现浮点误差时怎么取整Redis ZINCRBY 计数出现浮点误差时怎么取整
上一篇
Redis ZINCRBY 计数出现浮点误差时怎么取整
Go utf8.RuneStart 怎么在字节切片中找到字符边界
下一篇
Go utf8.RuneStart 怎么在字节切片中找到字符边界
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    57次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    212次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    143次使用
  • AI Prompt Library:免费AI提示词库,助力ChatGPT高效创作与营销
    AI Prompt Library
    探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
    75次使用
  • Generrated:DALL·E 2/3 AI绘画提示词灵感库与图像对比平台
    Generrated
    Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
    55次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码