Redis Lua 脚本中不能用 string.format,因其内置 Lua 5.1 环境默认禁用该函数以保证纯函数性;需用 string.gsub、table.concat 等替代方案安全拼接 key 或 JSON 片段,并严格校验 ARGV 输入合法性。

Redis Lua 脚本里不能用 string.format?
是的,Redis 内置 Lua 环境(Lua 5.1)**默认禁用 string.format、os.time、math.random 等函数**,这是为了保证脚本的纯函数性与可重现性。直接调用会报错:@user_script:1: attempt to call a nil value (field 'format')。
实际拼接需求(比如构造缓存 key、组装 JSON 片段、生成带时间戳的 token)必须绕过这个限制:
- 用
string.gsub+string.rep模拟简单模板(如"user:%s:profile"→string.gsub("user:%s:profile", "%s", uid)) - 用
table.concat替代多次..拼接,避免字符串拷贝开销(尤其在循环中) - 提前把固定前缀/后缀存进
KEYS或ARGV,减少脚本内硬编码
如何安全高效地拼接带变量的 Redis key?
常见错误是直接 "prefix:" .. ARGV[1] .. ":suffix",这在 ARGV[1] 为 nil 或含非法字符(如空格、花括号)时会导致 key 错误或命令失败。
正确做法:
- 始终对输入做非空和合法性校验:
if not ARGV[1] or type(ARGV[1]) ~= "string" or ARGV[1]:find("[%s{}%[%]]") then return error("invalid arg") end - 用
string.match提取数字 ID 类参数,避免拼接出"user:abc123:score"这类无效 key - 多级 key 优先用
table.concat:比如local key = table.concat({KEYS[1], ARGV[1], "status"}, ":"),比嵌套..更清晰且性能略优
需要动态生成 JSON 字符串怎么办?
Redis Lua 不支持原生 JSON 库,cjson 也未内置。强行手写 JSON 拼接极易出错(引号转义、nil 处理、嵌套结构)。
更务实的方案:
- **尽量避免在 Lua 中生成完整 JSON**——把结构化数据拆成多个字段,用
HSET分别存,读时再组合 - 若必须返回 JSON 片段,用
string.format的替代方案:string.gsub('{"id":%s,"name":"%s"}', "%%s", function() return i, j end)(需手动配对参数,慎用) - 对简单对象,用
table.concat+ 手动加引号:table.concat({'{"code":', ARGV[1], ',"msg":"', ARGV[2], '"}'),但必须确保ARGV[2]已预处理(如用string.gsub(ARGV[2], '"', '\"'))
为什么 table.concat 比 .. 更适合循环拼接?
Lua 中 a = a .. b 每次都会创建新字符串,时间复杂度 O(n²);而 table.concat 是 O(n),尤其在拼接 10+ 字段时差异明显。
典型场景(批量设置带序号的 key):
local parts = {}
for i = 1, #ARGV, 2 do
table.insert(parts, "user:" .. ARGV[i] .. ":score")
table.insert(parts, ARGV[i + 1])
end
redis.call("MSET", unpack(parts))
注意:unpack 在 Lua 5.1 中可用,但 Redis 7.0+ 的 Lua 5.4 环境需改用 table.unpack —— 实际写脚本前先查清服务端 Redis 版本。
真正容易被忽略的是:Lua 表索引从 1 开始,ARGV 和 KEYS 也是,但人肉数错下标导致 nil 参与拼接,最终 key 变成 "user:nil:score",这种 bug 很难 debug。











