Redis集群中EVAL报CROSSSLOT错误是因为脚本涉及的多个key未落在同一slot;解决方法是使用哈希标签{}使相关key强制映射到相同slot,如user:{1001}:profile。

为什么集群里一跑多KEY脚本就报CROSSSLOT
因为Redis集群根本不会让脚本跨slot执行——这不是bug,是设计硬约束。集群把16384个slot分给不同节点,EVAL命令发出去时,Redis必须提前知道该路由到哪个节点;而决定路由的唯一依据,就是KEYS数组里所有key的slot是否一致。只要KEYS[1]和KEYS[2]算出的slot不同,立刻拒绝,报CROSSSLOT Keys in request don't hash to the same slot。
常见错误现象:
- 本地单机Redis测试全过,一上集群就崩
-
EVAL "return {redis.call('GET', KEYS[1]), redis.call('GET', KEYS[2])}" 2 user:1001 order:999在集群里必错 - 用
redis-cli --cluster check发现同业务ID的key实际落在不同slot上
怎么让多个key强制落到同一个slot
唯一官方支持的方法是用{}哈希标签,且必须在写入数据时就定好,不能靠Lua脚本里拼接。
正确做法:
- key命名统一带业务ID标签:
user:{1001}:profile、order:{1001}:items、cart:{1001}:pending - 确保所有参与脚本的key都用同一对
{}包裹内容(比如都含1001) - EVAL调用时,
KEYS参数必须显式传入这些带标签的key,例如:EVAL "..." 3 user:{1001}:a order:{1001}:b cart:{1001}:c
错误写法:
-
user:1001:profile(整个字符串参与哈希,无法保证同slot) -
user:{1001}:a和{1001}:b混用(语义不一致,易出错) - 脚本里硬编码
redis.call('GET', 'user:1001:a')(没走KEYS,直接被判定为“non-local key”)
哪些情况Hash Tag也救不了你
Hash Tag只解决key路由问题,不改变命令能力边界。
明确不可行的场景:
-
SCAN或KEYS命令:集群模式下已被禁用,加{}无效 - 单key命令强行凑多key:比如
INCR、LPOP本身只接受一个key,无法通过Tag变成多key原子操作 - 跨用户批量统计:如后台要扫1000个不同
user:{id},哪怕每个都带Tag,它们的id不同 → slot必然不同 → 仍报CROSSSLOT
此时必须放弃Lua原子性,改用客户端分页+多次单key请求,再聚合结果。
上线前最容易忽略的验证点
光按规则起名不够,必须实测slot分布是否真一致。
务必执行:
-
CLUSTER KEYSLOT user:{1001}:profile和CLUSTER KEYSLOT order:{1001}:items—— 确认返回值相同 - 用
redis-cli --cluster check看目标slot是否确实由同一节点负责 - 扩容或rebalance后,重新验证——slot映射可能已变,旧Tag不一定还有效
没验证过slot分布就上线,等于把定时炸弹埋进生产环境。











