redis lua脚本禁止使用全局变量,所有变量必须用local声明;keys和argv是只读参数表,不可修改;需手动处理argv类型转换与nil默认值。

Redis Lua 脚本中**不允许使用全局变量**——这不是“怎么引用”的问题,而是根本不能用。任何未加 local 声明的变量(比如直接写 a = 1)在 Redis 的 Lua 环境里会被拒绝执行,报错 ERR Error running script (call to f_...): user_script:1: attempt to assign a nil value to global 'a'。
为什么 Redis 禁止全局变量
Redis 的 Lua 解释器被严格沙箱化,目的就是防止脚本之间互相污染、避免内存泄漏或状态残留。每个脚本运行在独立的 Lua state 中,但全局环境是共享的——如果允许写全局变量,一个脚本改了 math 或覆盖了 redis 表,后续所有脚本都会崩。
- 所有变量必须显式声明为
local -
KEYS和ARGV是唯二预定义的全局表,且只读(你不能KEYS = {}) -
redis表也是只读的,只能调用其方法(如redis.call),不能赋值或删除字段
常见误写:把局部变量漏写 local
这是新手最常踩的坑。比如想存一个 key 名做中间计算,写成:
key_name = KEYS[1] -- ❌ 错误:全局赋值
实际应写成:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
local key_name = KEYS[1] -- ✅ 正确
其他典型错误包括:
-
result = redis.call('GET', KEYS[1])→ 应为local result = redis.call('GET', KEYS[1]) -
for i = 1, #KEYS do ... end→i未声明,应为for local i = 1, #KEYS do ... end(Lua 5.2+ 支持 for 中声明,但 Redis 内置的是 Lua 5.1,必须提前声明) -
local tmp; tmp = redis.call(...)→ 拆成两行没问题,但若漏掉local就直接失败
KEYS 和 ARGV 不是“全局变量”,而是只读上下文参数
KEYS 和 ARGV 看起来像全局变量,但它们是 Redis 注入的只读 table,索引从 1 开始(不是 0),长度由 EVAL 命令的 numkeys 和后续参数数量决定。你不能:
-
KEYS[1] = "newkey"(报错:attempt to index a nil value) -
table.insert(KEYS, "extra")(会失败,且不推荐修改) -
local keys_copy = KEYS(浅拷贝,仍指向同一 table;如需副本,用local keys_copy = {unpack(KEYS)})
真正安全的做法是立即提取到局部变量:
local my_key = KEYS[1] local my_arg = ARGV[1] local ttl = tonumber(ARGV[2]) or 3600
容易被忽略的细节:nil 判断和默认值
因为 ARGV 里的值全是字符串,哪怕传的是数字或空值,也得手动转换。而且 ARGV[2] 可能根本不存在——访问越界不会报错,但返回 nil。所以:
- 不要直接
redis.call('EXPIRE', KEYS[1], ARGV[2]),先判空:if ARGV[2] then redis.call('EXPIRE', KEYS[1], tonumber(ARGV[2])) end -
not ARGV[1]和ARGV[1] == nil等价,但ARGV[1] == ""是 false(空字符串是真值) - 想设默认值,用
local timeout = tonumber(ARGV[1]) or 60,不能写tonumber(ARGV[1]) or 0——因为tonumber("")返回nil,而tonumber("0")是 0,逻辑要分清










