Redis Cluster 不支持跨 Slot 的批量命令,因所有 key 必须落在同一 Slot,否则报 CROSSSLOT 错误;需按 Slot 分组手动拆分请求,或用哈希标签(如 user:{1001}:profile)确保同 Slot。

Redis Cluster 不支持跨 Slot 的批量命令(如 MGET、MSET、DEL),报错 CROSSSLOT Keys in request don't hash to the same slot —— 这不是配置问题,是架构限制,必须按 Slot 拆分请求。
为什么 MGET user:1 user:2 order:100 一定报错
Redis Cluster 要求一个命令里所有 key 必须落在同一个 Slot。而 user:1、user:2、order:100 经 CRC16(key) % 16384 计算后大概率散落在不同 Slot。集群节点收到请求时会直接拒绝,不转发、不重试。
- 错误信息固定为:
CROSSSLOT Keys in request don't hash to the same slot - 哪怕只差 1 个 key 跨 Slot,整个命令就失败
- 客户端 SDK(如 Jedis、redis-py)通常不会自动拆分,需业务层处理
手动计算 Slot 的三种可靠方式
关键不是“怎么算”,而是“算得准、能复用、不依赖 Redis 实例”。推荐优先使用语言原生 CRC16 实现,避免调用 CLUSTER KEYSLOT 命令(多一次 RTT,且无法离线预判)。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- Python:用
crcmod库(注意多项式必须是crc-16-xmodem,不是modbus或ibm)import crcmod<br>crc16 = crcmod.predefined.mkPredefinedCrcFun('xmodem')<br>def key2slot(key): return crc16(key.encode()) % 16384<br>print(key2slot("user:1")) # 输出如 5459 - Go:直接用
github.com/redis/go-redis/v9/internal/hashtag.Slot(稳定、与官方一致)slot := hashtag.Slot("user:1") - Java:可用 Apache Commons Codec 的
Crc16XModem,或手写查表法(多项式0x1021,初始值0x0000)
批量操作必须按 Slot 分组再发送
拿到一批 key 后,不能直接塞进 MGET,要先分组、再并发执行。否则就是把错误当功能用。
- 分组逻辑:对每个 key 调用
key2slot(),按结果值聚合成 map[int][]string - 并发安全:每个 Slot 组独立发请求,可并行但不要无限制开 goroutine / thread
- 示例(Python):
from collections import defaultdict<br>keys = ["user:1", "user:2", "order:100", "profile:alice"]<br>slot_map = defaultdict(list)<br>for k in keys:<br> slot_map[key2slot(k)].append(k)<br>for slot, ks in slot_map.items():<br> # 连接该 slot 对应的节点(需维护 slot→node 映射)<br> result = redis_client.execute_command("MGET", *ks) - 注意:
slot → node映射必须缓存并定期更新(监听MOVED/ASK响应或定时CLUSTER SLOTS)
真正省事的办法:用 {} 强制哈希标签
如果这批 key 本就属于同一业务实体(比如都是用户 1001 的数据),在设计阶段就加哈希标签,比运行时拆分更干净。
- 改 key 名为:
user:{1001}:profile、user:{1001}:session、user:{1001}:settings - 此时
CRC16("1001") % 16384对三者结果完全一致,MGET可直接用 - 代价是:所有带
{1001}的 key 都绑定到同一个节点,要防止单节点过载 - 不适用于泛查询场景(如后台导出所有
order:*),这类只能硬拆
最易被忽略的是 slot→node 映射的时效性:它不是静态配置,节点故障、扩容、迁移都会导致变化。缓存过期时间设太长,就会持续往旧节点发请求,反复收到 MOVED;设太短,又频繁触发全量同步。折中做法是监听集群事件 + 设置 30 秒软过期 + 首次 MOVED 立即刷新。

















