redis集群下lua脚本报错主因是keys跨slot或evalsha不支持:crossslot因所有key必须同槽,需用{xxx}哈希标签确保;evalsha不支持因jedisclusterconnection默认禁用,须改用redistemplate.execute(redisscript, list, object...)并确保keys同槽。

Redis集群模式下执行Lua脚本报错,90%以上是 CROSSSLOT 错误或 EvalSha is not supported in cluster environment,根本原因不是脚本写得不对,而是 key 设计或调用方式没适配集群约束。
为什么EVAL总报CROSSSLOT Keys in request don't hash to the same slot
Redis集群把16384个slot分给不同节点,Lua脚本要求所有 KEYS 参数必须落在同一slot——否则连脚本解析都不做,直接拒绝。单机环境测通、上集群就崩,就是这个原因。
- 典型错误写法:
"user:1001:profile"和"order:1001:items"—— 整个字符串参与CRC16计算,基本不可能同slot - 正确解法:用
{}包裹业务标识符,只让这部分参与哈希,比如"user:{1001}:profile"和"order:{1001}:items" - 注意嵌套
{}只取最内层第一对,"user:{abc{def}:xyz"实际按"def"计算slot -
{}不能为空,"user:{}:profile"会被当成完整key处理,失去控制力
Spring中用RedisTemplate.execute()为何抛EvalSha is not supported
Spring Data Redis的 JedisClusterConnection 默认禁用 EVALSHA,因为集群环境下它无法保证脚本已预加载到目标节点。直接调用 execute() 会走 evalSha 分支,触发该异常。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 绕过方案:改用
RedisTemplate.execute(RedisScript, List<string>, Object...)</string>重载方法,传入明确的KEYS列表(不是Collection),Spring内部会尝试slot对齐 - 必须用
DefaultRedisScript包装脚本,并显式设置resultType,否则返回值类型错乱 - 脚本里所有key必须通过
KEYS[1]等方式传入,禁止硬编码;Spring不帮你做hash拆分,全靠你确保key同slot - 集群模式下,
execute()不自动重试MOVED/ASK重定向,网络抖动时脚本状态不可知,容易丢数据
如何让Lua脚本在集群里真正原子且高效
原子性本身由Redis保障(单线程执行脚本),但“真正可用”还需避开几个隐形坑:
- 避免在脚本里反复调
redis.call(),比如读同一个key三次,应存为局部变量复用 - 批量操作优先用原生命令:
HMGET替代循环HGET,MSET替代多次SET - 禁用耗时计算:JSON解析、正则匹配、大数组排序、
os.date()格式化——这些都可能超lua-time-limit(默认5秒) - 高频调用务必用
SCRIPT LOAD+EVALSHA,减少网络传输,但前提是所有key已满足同slot条件
最易被忽略的一点:哈希标签 {} 不是Lua脚本的语法糖,它是key设计阶段就必须定死的约定。上线后补加 {} 意味着老key失效、迁移成本陡增——这事得在建模时就想清楚,而不是等报错再改。










