当前位置: 首页
数据库
Redis Lua脚本禁用动态Key须遵循KEYS数组传递参数规范

Redis Lua脚本禁用动态Key须遵循KEYS数组传递参数规范

热心网友 时间:2026-07-22
转载

在Redis集群中,使用Lua脚本时,key必须通过KEYS数组硬编码传递,不能赋值给变量或使用ARGV,否则在预检阶段无法进行slot校验,导致脚本执行失败。所有key必须属于同一个slot,否则会报错;可用{tag}强制将它们分配到相同slot才能执行。

Redis 集群下的 Lua 脚本:为什么不能把 Key 写在变量里?

这是一个相当经典的问题。简单说,在 Redis 集群环境中,Lua 脚本里所有要操作的 key 必须通过 KEYS[1]KEYS[2] 这种字面量直接引用,不能先赋值给变量再用。否则就会抛出一个让人头疼的错误:ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array

根本原因在于,Redis 集群在执行脚本之前,需要做一次静态分析,找到脚本要操作的所有 key 分别属于哪个 slot,确保它们都在同一个分片上。而这个分析只能识别硬编码的 KEYS[n],不会去执行 Lua 变量解析。所以一旦你写成 local k = KEYS[1]; redis.call("GET", k),Redis 在预检阶段就彻底懵了——它不知道 k 到底指向哪个 key,slot 一致性无法保证,直接拒绝执行。

为什么 Redis 集群里 Lua 脚本必须用 KEYS[1] 而不能赋值给变量再用

这个问题已经解释了一半。再强调一遍:slot 校验是脚本加载前的必需步骤,不是可选的。它不依赖 Lua 执行逻辑,纯粹是语法层面的扫描。正因为如此,阿里云 Redis、腾讯云 CRS、AWS ElastiCache 等主流集群版都强制要求遵守这个规则。而单机版 Redis 压根不校验 slot,所以同样的脚本在本地可能跑得欢,一上集群就挂。这不是什么性能优化,而是路由前提——没有这一步,Redis 根本不知道该把脚本调度到哪个节点。

  • 常见误区:以为“反正我脚本里只有一条命令,用变量也没关系”——错,校验阶段就过不去。
  • 正确的做法:所有 key 名必须原生出现在 KEYS 数组里,脚本里也只能以 KEYS[1] 这种形式硬引用。

KEYS 数组里放错一个 key 就会报 ERR eval/evalsha command keys must in same slot

集群要求所有 KEYS 里的 key 必须属于同一个 slot,否则脚本连解析都过不去。Redis 通过 CRC16(key) % 16384 计算 slot,结果虽然看似随机,但实际上是可预测的。比如 user:1001user:1002 未必在同一个 slot 上,它们 hash 后很可能落在不同分片。如果你确实需要批量操作多个 key,就必须用 {tag} 强制它们对齐到同一个 slot。比如 user:{1001}:profileuser:{1001}:settings 就属于同一个 slot,因为花括号内的 1001 会被当成 hash 的唯一依据。验证方法也很简单:用 redis-cli --cluster checkCLUSTER KEYSLOT 看看 slot 分布就清楚了。

为什么 ARGV 不能当 key 用,哪怕它传的是字符串

ARGV 是专门用来传递数据参数的,Redis 明确禁止在 redis.call() 的 key 参数位置使用它。即使 ARGV[1] 的值是 "mykey",写成 redis.call("GET", ARGV[1]) 也会触发同样的错误。根本原因一样:ARGV 的值要到脚本运行时才确定,预检阶段 Redis 根本没法提取内容来做 slot 校验。

一个很常见的翻车场景:用户想“复用脚本”,把 key 名也当成参数传,结果本该属于 KEYS 的值被塞进了 ARGV。正确做法是:key 名必须出现在 EVAL 命令的 key 列表中(决定 KEYS 内容),而且脚本里只能以 KEYS[1] 形式硬引用。

EVAL 命令参数顺序错一位,整个 KEYS/ARGV 映射就全乱

EVAL 命令的结构是:EVAL