必须用 local 声明所有变量,否则因 lua 5.1 不回收全局变量导致内存滞留和并发覆盖;for 循环变量需显式声明 local;推荐 table.new 预分配及索引赋值替代 table.insert;局部变量需显式初始化防残留。

必须用 local 声明所有变量,否则脚本会因全局污染和内存滞留引发隐性泄露。
为什么不用 local 会导致内存问题
Redis 的 Lua 环境基于 Lua 5.1,不回收全局变量;一旦脚本中出现 a = 1 这类赋值,变量 a 就挂载在全局环境里,后续所有脚本都能读写它。多次执行后,残留的全局表、字符串、数组不会被 GC 清理,used_memory 持续上涨——这不是传统意义的“内存泄露”,而是环境污染导致的不可控内存滞留。
更危险的是:多个并发脚本若共用同名全局变量(如都写 result = {}),会相互覆盖、逻辑错乱,且难以复现。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
local 声明的正确写法与常见错误
所有变量必须显式加 local,包括循环计数器、临时结果、中间表:
- ✅ 正确:
local i = 1、local buf = {}、local val = redis.call("GET", KEYS[1]) - ❌ 错误:
i = 1(自动变成全局)、buf = {}(污染全局)、for i = 1, #KEYS do(i 是全局!) - ⚠️ 特别注意:
for循环中的控制变量默认是全局,必须提前声明:local i; for i = 1, #KEYS do
配合 table.new 预分配避免动态扩容抖动
即使用了 local,频繁 table.insert(t, x) 或 t[#t + 1] = x 仍会触发 Lua 内部 table 扩容,产生大量临时内存碎片。Redis 8.2+ 支持 Lua 5.2+ 的 table.new,应优先使用:
- 预估大小时:
local buf = table.new(1000, 0)(1000 个元素,0 个哈希槽) - 不确定大小但有上限时:
local buf = table.new(math.min(#KEYS, 500), 0) - 禁用
table.insert,改用索引赋值:buf[i] = val - 拼接字符串时,也用
table缓存:local parts = table.new(100, 0); parts[i] = "item"; return table.concat(parts, "")
容易被忽略的边界点
局部变量只保留在当前脚本生命周期内,但若你用 redis.pcall 捕获异常后没清空中间状态(比如 local res = {} 在 try 块里填充,catch 块里没重置),下次调用时该变量仍是上次的残留值——因为 local 变量作用域是整个函数体,不是每个语句块。所以涉及条件分支或异常路径的变量,务必显式初始化:
- ❌ 危险:
local data; if cond then data = redis.call(...) end; return data(data 可能为 nil,但上一轮残留值可能还在) - ✅ 安全:
local data = nil; if cond then data = redis.call(...) end; return data










