当前位置:首页 > 文章列表 > 数据库 > Redis > Redis Lua 脚本怎样通过 KEYS 和 ARGV 区分参数

Redis Lua 脚本怎样通过 KEYS 和 ARGV 区分参数

来源:17golang原创 2026-10-23 05:00:43 0浏览 收藏

我在排查一段 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,即使第二个参数本来只是业务状态。

Redis EVAL 参数区间中 numkeys 将 key 参数与普通参数分开的静态结构说明图
图1:EVAL 参数边界说明图,查看 numkeys 如何把 key 名称分配给 KEYS、把其余输入分配给 ARGV;这是静态说明图,不是运行截图。

在 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 列表中按固定顺序传入,并在注释中写清每个下标的含义。

Redis Lua 客户端将 Go 的 Keys 和 Args 映射到 Lua KEYS 与 ARGV 的静态关系说明图
图2:客户端参数契约说明图,展示 Go 调用端、EVAL 边界和 Lua 运行时两个参数表的对应关系;这是静态说明图,不是运行截图。

集群场景要守住 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 脚本的参数契约就会清晰很多。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Redis ZSET 怎样查询分数区间内的成员Redis ZSET 怎样查询分数区间内的成员
上一篇
Redis ZSET 怎样查询分数区间内的成员
Redis SCAN 为什么不能当作一次性全量遍历
下一篇
Redis SCAN 为什么不能当作一次性全量遍历
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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模型性能。
    603次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    643次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    673次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    639次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    460次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码