jediscluster调用eval()直接报crossslot错误,因redis集群强制要求keys数组中所有key必须落在同一slot,否则服务端拒绝执行并返回non-local-key或crossslot错误;硬编码key、跨slot传参、非法hash tag(如缺右括号)均会触发该限制,且evalsha同样受此约束。

为什么JedisCluster调用eval()直接报CROSSSLOT错误
因为Redis Cluster强制要求:所有出现在KEYS数组里的key,必须落在同一个slot上,否则拒绝执行。而JedisCluster.eval()会尝试把脚本发给任意节点(取决于第一个key的slot),但脚本里如果硬编码了其他key、或传入的KEYS参数本身跨slot,服务端立刻返回ERR Lua script attempted to access a non local key in a cluster node。
这不是Jedis的bug,是Redis集群的硬性限制——它不支持跨节点原子操作,Lua也不能绕过。
-
KEYS[1]和KEYS[2]的slot不同 → 必报CROSSSLOT - 脚本里写死
redis.call('GET', 'user:1001'),但该key没出现在KEYS数组中 → 报non local key - 即使只传一个key,但key格式不合法(如
user{1001缺右括号)→ slot计算不可控,可能意外跨槽
怎么让多个key落到同一slot:必须用合法的Hash Tag
Redis只对第一个成对{}之间的内容做哈希计算,其余部分被忽略。所以你要在设计阶段就统一key命名规则,而不是等写脚本时再拼接。
- ✅ 正确:
{order:12345}:status和{order:12345}:items→ 都哈希order:12345,同slot - ❌ 错误:
order:12345:status和order:12345:items→ 全字符串哈希,大概率跨槽 - ⚠️ 易错:
order:{12345}:status虽然合法,但语义模糊;建议统一用{order:12345}:xxx风格 - 上线前务必用
redis-cli --cluster check验证打标后的key是否真落在同一slot,扩容后可能失效
JedisCluster里evalsha()也报错?不是缓存了就能跑
EVALSHA只是省带宽,它不改变slot校验逻辑。只要KEYS数组跨槽,哪怕脚本已用SCRIPT LOAD预加载,照样报CROSSSLOT。
-
SCRIPT LOAD本身不校验slot,只存脚本 → 它成功不代表能执行 - Jedis默认不会自动
SCRIPT LOAD,你得自己在应用启动时显式加载,或每次用eval()触发(但会放大网络开销) - 如果用
evalsha(),必须确保:① 脚本已加载到目标节点;②KEYS全部同slot;③ SHA值传对(大小写敏感) - 别依赖“脚本内容不变SHA就不变”——Windows换行符
\r\n会导致SHA变化,Linux下执行报NOSCRIPT
最容易被忽略的实操细节
很多团队卡在最后一步:以为加了{}就万事大吉,结果上线后偶发失败。
- 客户端(如Jedis)无法自动发现slot变更并重试,它只会按
KEYS[1]路由,后续key校验失败就直接抛异常 - 批量操作多个无关用户数据(比如后台统计)时,Hash Tag救不了你——这种场景根本不该强求Lua原子性,改用多次单key请求+客户端补偿更稳
- Lua里不能动态构造新key(如
KEYS[1]..':backup'),因为新key没经过slot校验,运行时报non local key - 阿里云Redis集群版、腾讯云CRS等托管服务同样遵循该限制,不存在“兼容模式”绕过










