当前位置:首页 > 文章列表 > 数据库 > Redis > Redis Lua 里用 ARGV 传 JSON 时怎么避免类型误判

Redis Lua 里用 ARGV 传 JSON 时怎么避免类型误判

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

Redis Lua 里通过 ARGV 传入 JSON 时,最稳妥的做法是把它当作原始字符串接收,只在脚本入口用一次 cjson.decode,随后按对象和字段逐个校验类型。不要把“客户端传来的 JSON 字符串”“解码后的 Lua 表”和“要写入 Redis 的字符串”混成一个变量。这样既能保留脚本的原子性,也能避免数字、字符串、布尔值和空值在不同边界发生误判。

要点速览
  • KEYS 只放脚本实际访问的键,业务 JSON 放在 ARGV
  • ARGV[1] 先按字符串处理,cjson.decode 只做一次,并用 pcall 兜住非法 JSON。
  • 写回 Redis 前重新编码对象,返回给客户端时明确区分字符串、整数和错误表。

ARGV 到底是什么:先把原始字符串留在边界外

Redis Lua 中 ARGV 原始字符串经过 cjson.decode 进入对象字段校验的静态边界关系
图1:ARGV、JSON 解码和字段校验是三个不同边界,先保留原文再进入 Lua 对象层。

EVAL script numkeys key [key ...] arg [arg ...] 中,前面的键名进入 KEYS,后面的普通参数进入 ARGV。因此一次调用可以写成下面这样:

-- KEYS[1] 是脚本要操作的键,ARGV[1] 是完整 JSON 字符串
EVAL "return ARGV[1]" 1 order:1001 '{"amount":12,"source":"api"}'

这里的 ARGV[1] 仍然是字符串。它不是 Lua 表,也不会因为内容长得像数字就自动变成数字。真正的类型变化发生在 cjson.decode 之后:JSON 对象会成为 Lua 表,JSON 数字会成为 Lua number,JSON 字符串仍是 Lua string。把这个边界写清楚,后面的判断才有依据。

先解码一次,再按业务类型收口

生产脚本不要到处重复解码,也不要拿 tostring 代替类型校验。下面的入口只接受一个 JSON 对象,并要求 amount 是数字、source 是字符串;任何不符合约定的输入都会在写入前结束。

-- 入口只负责解析和收口,不把未校验的字段写入 Redis
local raw = ARGV[1]
if type(raw) ~= "string" or raw == "" then
    return { err = "ARGV[1] must be a non-empty JSON string" }
end

-- cjson.decode 可能抛出异常,用 pcall 把坏输入变成可识别的错误
local decoded_ok, payload = pcall(cjson.decode, raw)
if not decoded_ok or type(payload) ~= "table" then
    return { err = "payload must be a JSON object" }
end

local amount = payload.amount
local source = payload.source
if type(amount) ~= "number" or type(source) ~= "string" then
    return { err = "amount must be number and source must be string" }
end

-- Redis 命令参数使用明确的字符串表示,避免把 Lua 表直接传给 HSET
redis.call("HSET", KEYS[1], "amount", tostring(amount), "source", source)
return { "ok", tostring(amount) }

如果业务允许 amount 既可以是 JSON 数字也可以是数字字符串,要在协议层明确选择,而不是在脚本里无条件转换。否则 12"12" 会悄悄失去区别,调用方也无法判断这是客户端传错类型,还是服务端主动兼容。

KEYS、ARGV 和返回值要分开设计

Redis Lua 中 KEYS 与 ARGV 进入原子脚本、命令写入和响应返回的静态结构
图2:KEYS 负责键名,ARGV 负责业务参数,Redis 命令和脚本返回值各自保持明确类型。

JSON 对象不能直接作为 Redis 命令的一个参数。需要保存整个对象时,先在 Lua 中完成校验,再用 cjson.encode 重新得到字符串;只保存字段时,则把每个字段显式转为命令接受的值。脚本返回值也要提前约定,因为 RESP2 下 Lua number 会按整数回复,带小数的结果不能直接这样返回。

-- 将已校验的数据重新编码后保存,避免把 Lua table 当成命令参数
local record = {
    amount = amount,
    source = source,
    state = "accepted"
}
redis.call("SET", KEYS[1], cjson.encode(record))

-- 用字符串返回小数或需要保持格式的值,避免 RESP2 的整数转换
return {
    status = "ok",
    value = cjson.encode(record)
}

上面的关联表返回方式适合在脚本内部表达状态,但客户端看到的最终结构仍受 RESP2/RESP3 协议转换影响。若接口需要稳定的跨客户端响应,建议返回短字符串或定长数组,例如 {"ok", cjson.encode(record)},并在客户端统一解析。

发布前用边界清单挡住类型误判

检查位置应保持的类型常见错误
KEYS真实键名把 JSON 字段拼成动态键,集群路由和审计都变得不清晰
ARGV原始字符串同一参数重复解码,或未校验就写入
Lua 表脚本内部对象直接作为 HSET/SET 参数传递
返回值已约定的字符串/数组把小数 Lua number 当作精确小数返回

上线前至少检查四件事:所有访问的键是否都在 KEYS;JSON 是否只解码一次;每个写入字段是否经过 type 判断;返回值是否在目标 RESP 协议下仍保持客户端约定。脚本仍然是一次原子执行,但原子性不会替你修复输入类型,边界校验必须放在命令写入之前。

常见问题

ARGV[1] 是不是可以直接当 Lua 表使用?

不可以。它先是客户端传入的字符串,必须经过 cjson.decode 才会得到 Lua 值;解码前应先检查空串和输入来源。

JSON 对象为什么不能直接传给 HSET?

解码后的对象是 Lua table,不是 Redis 命令参数。要么取出字段并逐项转换,要么用 cjson.encode 把完整对象重新变回字符串。

为什么返回小数时建议转字符串?

Lua 只有一种 number 类型,RESP2 返回 Lua number 时会按整数规则转换。需要保留小数文本时,应按字符串返回并由客户端解析。

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