登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

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

来源:17golang原创

时间:2026-09-10 03:32:46 423浏览 收藏

库存扣减、额度占用这类逻辑,不能先在客户端 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删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>