eval或evalsha总报crossslot错误,因redis集群强制要求所有keys参数必须落在同一slot,否则服务端直接拒绝执行;须用{}哈希标签使多个key共享同一槽位,如{user:1001}:profile和{user:1001}:settings均只对user:1001计算slot。

为什么EVAL或EVALSHA总报CROSSSLOT错误
因为Redis集群强制要求Lua脚本中所有KEYS参数必须落在同一slot,否则直接拒绝执行——连脚本解析都不做。错误信息典型长这样:(error) CROSSSLOT Keys in request don't hash to the same slot 或 ERR 'EVAL' command keys must in same slot。这不是客户端能绕过的限制,是服务端硬性校验。
单机Redis不检查这个,所以本地跑通、上集群就崩,非常容易漏掉。Jedis/Lettuce等客户端会在调用前用JedisClusterCRC16.getSlot()逐个算slot,不一致就抛JedisClusterException。
-
EVALSHA不是“绕过”方案:它只发sha1值,校验逻辑和EVAL完全一致,仍强制同slot - 脚本里硬编码
"user:1001"没用——只要没出现在KEYS数组里,就是“非本地key”,直接被拒 - 服务端生成key时就得带tag,别指望Lua里动态拼接新key来“对齐slot”(Lua里做不到)
怎么用{}哈希标签让多个key落到同一slot
Redis计算slot时,只取第一个成对{}里的内容做CRC16,其余部分全忽略。比如{user:1001}:profile和{user:1001}:settings都只对user:1001哈希,必然同slot。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 正确写法:
{order}:1001、{order}:2001(都哈希order);user:{1001}:profile、cart:{1001}:pending(都哈希1001) - 错误写法:
user{1001}:profile(没闭合,整个key参与哈希,行为不可控);user:{}:profile(空括号,退化为全key哈希) - 嵌套
{}只认最内层第一对:user:{abc{def}:xyz实际取def算slot - gocraft/work这类库,只需把命名空间改成
"{my_app_namespace}"即可生效
哪些场景Hash Tag也救不了你
Hash Tag只解决key路由问题,不改变命令本身能力。有些命令天生就是单key的,加了{}也没法变多key操作。
-
INCR、LPOP、LPUSH等只接受一个key的命令,无法通过Hash Tag“变”成多key操作 -
SCAN、KEYS在集群模式下被完全禁用,Hash Tag不可绕过 - 如果业务必须读写多个无关用户数据(比如后台批量统计),别强撑Lua原子性——改用多次单key请求 + 客户端逻辑补偿
- 哈希标签会降低slot分散度,极端情况造成单slot热点(比如所有订单都打到
{1}),上线前必须用redis-cli --cluster check看实际分布
上线前最容易被忽略的验证动作
光靠命名规则不验证,扩容或rehash后可能突然出问题。必须手动确认打上相同tag的key真落在了同一个slot。
- 用
CLUSTER KEYSLOT {user:1001}:profile和CLUSTER KEYSLOT {user:1001}:settings分别查,结果必须一致 - 用
redis-cli --cluster check扫全量slot分布,重点看目标tag对应的slot是否负载均衡 - 客户端如Lettuce/Jedis虽内置ASKING处理,但前提是传入的key已满足同slot条件;否则重定向后仍失败
槽位对齐这事,得在key生成阶段就定死,不能留到调用脚本时再补救。










