Redis Lua 脚本怎样通过 KEYS 和 ARGV 区分参数
来源:17golang原创
时间:2026-10-23 05:00:43 313浏览 收藏
我在排查一段 Redis Lua 脚本时,最先怀疑的是脚本逻辑,最后发现问题出在调用参数的分界:业务值被放进了 KEYS,真正的 key 却没有按 numkeys 传入。结果不是简单的“取不到值”,而是脚本在单机环境里看起来能跑,换到集群或换一组参数后就变得难以判断。
记住一个规则就能拆开这类问题:KEYS 只放 Redis key 的名字,ARGV 放阈值、状态、新值等普通参数;两者的边界由 EVAL 的 numkeys 决定。
官方地址:https://redis.io/docs/latest/develop/programmability/eval-intro/
先看 EVAL 的参数边界
EVAL 的基本形式是 EVAL script numkeys [key ...] [arg ...]。脚本字符串之后的第一个数字不是“参数总数”,而是后面有多少个参数属于 key。Redis 从第三个参数开始读取 key 名称,读完 numkeys 个之后,剩下的才会进入普通参数区域。
# numkeys=1:user:1001 属于 KEYS[1],42 属于 ARGV[1]
redis-cli EVAL "return {KEYS[1], ARGV[1]}" 1 user:1001 42
# numkeys=0:没有 key,后面的文本全部是普通参数
redis-cli EVAL "return ARGV[1]" 0 hello
因此,不能把 numkeys 写成“后面一共有几个值”。如果写成 2,Redis 会把接下来的两个参数都放入 KEYS,即使第二个参数本来只是业务状态。

在 Lua 中分别读取 KEYS 和 ARGV
Redis 会在脚本执行上下文中预先提供两个表。KEYS[1] 到 KEYS[n] 对应显式传入的 key,ARGV[1] 到 ARGV[m] 对应剩余的普通参数。下标从 1 开始,不是 Go、JavaScript 那种从 0 开始。
-- KEYS[1] 是要读取的 key,ARGV[1] 是业务传入的最低分数
local current = redis.call('GET', KEYS[1])
local minimum = tonumber(ARGV[1])
-- key 不存在时返回明确结果,避免把 nil 当成普通数字比较
if not current then
return {0, 'missing'}
end
-- 只把业务参数用于判断,不把它拼进 key 名称
if tonumber(current) >= minimum then
return {1, current}
end
return {0, current}
这段代码里的职责是分开的:KEYS[1] 表示数据位置,ARGV[1] 表示判断条件。普通参数不要拼接成 key 再访问,否则既破坏了参数约定,也会让 Redis Cluster 无法提前知道脚本可能访问哪些 key。
用 redis-cli --eval 对照调用顺序
如果脚本放在 check-score.lua 中,redis-cli --eval 使用逗号分隔 key 区域和普通参数区域。逗号前的参数会进入 KEYS,逗号后的参数会进入 ARGV,这和 EVAL 的 numkeys 规则是同一件事的命令行写法。
# 第一个参数是 KEYS[1];逗号后才是 ARGV[1] redis-cli --eval check-score.lua account:1001 , 60 # 两个 key 要全部放在逗号前,普通阈值仍放在逗号后 redis-cli --eval compare.lua account:1001 account:1002 , 10
排查时可以先让脚本临时返回参数内容,确认边界后再换回实际逻辑。不要把日志、调试字符串或普通状态值放在 key 区域来“凑数量”,那会把错误隐藏到调用端。
在 Go 客户端固定参数职责
客户端封装最好把 key 和普通参数拆成两个明确的切片。以 go-redis 风格为例,Keys 切片会对应 KEYS,Args 切片会对应 ARGV;具体方法签名请按项目依赖版本调整。
// EvalScore 把 key 与普通阈值分开传给 Redis Lua 脚本。
func EvalScore(ctx context.Context, rdb *redis.Client, key string, minimum int64) (string, error) {
const script = `
-- KEYS[1] 只表示数据 key,ARGV[1] 表示业务阈值
local value = redis.call('GET', KEYS[1])
if not value then
return 'missing'
end
if tonumber(value) >= tonumber(ARGV[1]) then
return value
end
return 'below'
`
// Keys 与 Args 分开,避免把 minimum 误当作 key。
result, err := rdb.Eval(ctx, script, []string{key}, minimum).Result()
if err != nil {
// 保留 Redis 返回的错误,让调用方决定重试或记录。
return "", err
}
return result.(string), nil
}
多人协作时,命名比位置更可靠:调用函数时先写 key,再写 minimum;脚本内部只从 KEYS 取数据位置,只从 ARGV 取业务输入。若脚本需要多个 key,就在同一份 key 列表中按固定顺序传入,并在注释中写清每个下标的含义。

集群场景要守住 key 的边界
Redis 官方文档要求脚本访问的所有 key 名称都作为输入 key 显式提供,脚本不要根据字符串拼接或数据内容临时生成 key。原因是集群需要根据 key 识别路由范围;把 key 藏在 ARGV 或脚本内部,会让调用约定和集群路由都变得不可靠。
| 输入内容 | 放置位置 | 脚本访问方式 | 典型例子 |
|---|---|---|---|
| Redis key 名称 | KEYS 区域 | KEYS[1]、KEYS[2] | user:1001 |
| 阈值、状态、新值 | ARGV 区域 | ARGV[1]、ARGV[2] | 60、enabled |
| 脚本源码 | EVAL 第一个参数 | Lua 程序 | return redis.call(...) |
最后可以按三点复查调用:第一,numkeys 是否等于 key 的数量;第二,脚本中的每个 KEYS[i] 是否都能在调用端找到对应位置;第三,业务值是否全部从 ARGV 读取。三点一致后,再考虑脚本本身的返回值和异常处理。
常见问题
KEYS 和 ARGV 的下标从 0 开始吗?
不开始。Redis Lua 脚本中的两个表都按 Lua 习惯从 1 开始访问,例如第一个 key 是 KEYS[1],第一个普通参数是 ARGV[1]。
没有 key 时应该怎样传参?
把 numkeys 设为 0,脚本不要访问 KEYS,所有后续输入都会进入 ARGV。命令行写法则是在 --eval 后直接写脚本,再写逗号和普通参数。
可以把 key 名称放进 ARGV 再拼接吗?
不建议。需要访问的 key 应在调用时显式放入 KEYS,并保持固定顺序;这不仅让脚本更容易读,也符合 Redis Cluster 对脚本 key 输入的要求。
总结
numkeys 是 EVAL 的分界线:它先决定有多少个输入属于 key,Redis 再把这些值放到 KEYS,把剩余值放到 ARGV。调用端把 key 和普通参数分成两组,Lua 端按 1 开始的下标读取,集群场景中不动态生成 key,Redis Lua 脚本的参数契约就会清晰很多。
-
236 收藏
-
484 收藏
-
104 收藏
-
463 收藏
-
166 收藏
-
250 收藏
-
204 收藏
-
244 收藏
-
173 收藏
-
458 收藏
-
306 收藏
-
146 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习