redis lua脚本中只能用内置cjson库解析json,因其无需额外模块且兼容性好;而json.set/get需redis.json模块支持,未加载时会报错。

Redis Lua脚本里能解析 JSON,但必须用 cjson 库,且不能直接操作嵌套字段——它只提供 cjson.decode() 和 cjson.encode() 两个基础能力,所有字段访问、修改、合并都得靠 Lua 表操作手动完成。
为什么不能直接用 JSON.SET/JSON.GET?
因为 JSON.SET、JSON.GET 是 redis.json 模块的命令,不是原生 Redis 的一部分。如果你的 Redis 实例没加载该模块(比如标准社区版或旧版本),执行这些命令会报错:(error) ERR unknown command。而 cjson 是 Redis 内置的 Lua 库,只要 Redis ≥ 2.6 就可用,无需额外安装。
- 检查是否支持:
redis-cli EVAL "return cjson.decode('{}')" 0—— 成功返回空表说明可用 - 模块命令(如
JSON.GET user:1001 $.name)和cjson是两套体系,不能混用 - 模块命令原子性强、语法简洁;
cjson灵活但易出错,尤其在处理 null、数字精度、转义字符时
cjson.decode() 常见崩溃点
传入非法 JSON 字符串会导致 Lua 脚本直接中断,并返回 (error) ERR Error running script,而不是返回 nil 或错误信息。这不是 Redis 的 bug,是 cjson 的设计:它不捕获解析异常。
- 必须用
pcall包裹:local ok, res = pcall(cjson.decode, ARGV[1]),否则脚本一遇到"{name:"Alice"这种少引号的字符串就挂 -
cjson.decode()对null返回 Lua 的nil,但 Lua 表中nil无法被序列化回 JSON —— 后续cjson.encode()会丢掉该字段 - 浮点数可能丢失精度:
cjson.decode('{"x":1.0000000000000001}')['x']在 Lua 中变成1,再 encode 回去就是{"x":1}
安全更新嵌套字段的最小可行写法
想把 {"user":{"profile":{"age":25}}} 中的 age 改成 26,不能硬拼字符串,也不能忽略缺失路径。正确做法是分三步:解码 → 深层赋值(带路径检查)→ 编码回写。
local cjson = require("cjson")
local json_str = redis.call("GET", KEYS[1])
if not json_str then return nil end
<p>local ok, data = pcall(cjson.decode, json_str)
if not ok then return "invalid json" end</p><p>-- 手动走路径:user.profile.age
if data.user and data.user.profile then
data.user.profile.age = tonumber(ARGV[1]) or 0
else
return "missing path"
end</p><p>redis.call("SET", KEYS[1], cjson.encode(data))
return "ok"</p>
- 别用
string.gsub替换字段值——JSON 字符串里字段名可能出现在值中(如{"desc":"age=25"}),导致误改 - 路径访问必须逐层判空,Lua 中
data.user.profile.age = 26在profile为 nil 时静默失败,不会报错也不会生效 - 写回前务必
cjson.encode(),不要直接..拼接,否则中文、引号、反斜杠全乱
什么时候该放弃 Lua + cjson?
当你需要频繁做“读-改-写”且涉及条件判断(比如 if age > 18 then set status="adult")、数组追加、字段存在性校验,或结构深度超过 2 层时,cjson 就开始吃力了。这时真正该做的不是写更长的 Lua,而是换方案:
- 升级到 Redis Stack 或启用
redis.json模块,用JSON.SET key $.user.profile.age 26一行搞定 - 拆 key:把
user:1001的 profile 单独存为user:1001:profile,用 Hash 或 String 存,避免 JSON 解析开销 - 客户端解析:如果并发不高、延迟不敏感,GET + 客户端解析 + SET 更清晰,也更容易 debug
最常被忽略的一点:Lua 脚本里没有日志,出错了只能靠返回值猜——所以 pcall 不是可选项,是必选项;而任何对 cjson.encode() 的调用,都要假设它可能因循环引用或 unsupported type(如 function)而崩溃。










