Redis Cluster 是如何精准定位一个 Key 的?从 Slot 映射到 MOVED 重定向全解析

0 阅读4分钟

在 Redis Cluster 中,一个 SET user:1001:name Alice 命令,是如何被准确路由到正确的节点的? 如果发错了节点,又是谁在背后默默“指路”? 本文带你深入 Redis Cluster 的核心机制:Key → Slot → Node 的完整寻址链路

如果你用过 Redis Cluster,一定听说过 “16384 个槽”、“CRC16 哈希”、“MOVED 重定向”这些概念。但你是否真正理解:

  • 为什么 key 能稳定映射到同一个 slot?
  • 客户端怎么知道该连哪个节点?
  • 当 key 不在当前节点时,是服务器转发,还是客户端重试?

今天,我们就从 底层原理 出发,彻底讲清楚 Redis Cluster 的数据分布与路由机制。


一、Key → Slot:确定性哈希,全局一致

Redis Cluster 将整个 key 空间划分为 16384 个 slots(槽),编号 0 ~ 16383

每个 key 通过以下公式确定其所属 slot:

slot = CRC16(key) % 16384
  • CRC16 是一种快速、低冲突的哈希算法;
  • % 16384 确保结果落在有效槽范围内;
  • 最关键的是:这个计算是确定性的 —— 无论在哪台机器、哪个客户端,同一个 key 永远得到相同的 slot。

📌 示例:

key = "order:20240501"
→ CRC16("order:20240501") = 20000
→ slot = 20000 % 16384 = 3616

✅ 所有节点和客户端对 “key 属于哪个 slot” 达成共识,这是分布式一致性的基石。


二、Slot → Node:集群元数据,每个节点都“心中有图”

光知道 slot 还不够,还得知道 哪个节点负责这个 slot

Redis Cluster 采用 去中心化架构,没有中心协调服务(如 ZooKeeper)。那它是如何让每个节点都知道全局拓扑的?

答案是:Gossip 协议

每个节点都维护一份完整的集群视图

执行 CLUSTER NODES,你会看到类似输出:

a1b2c3... 192.168.1.10:7001 master - connected 0-5460
d4e5f6... 192.168.1.11:7002 master - connected 5461-10922
g7h8i9... 192.168.1.12:7003 master - connected 10923-16383

即使你在 192.168.1.10 上执行,也能看到其他节点的 slot 范围!

Gossip 如何同步?

  • 节点之间定期交换 PING/PONG 消息;
  • 消息中携带部分集群元数据(包括 slot 分配);
  • 信息像“流言”一样扩散,最终所有节点达成一致。

💡 这种设计避免了单点故障,也无需外部依赖。


三、客户端路由:智能缓存 + 本地计算

Redis Cluster 不要求客户端连接所有节点,而是依赖 智能客户端(如 Lettuce、JedisCluster、Redisson)。

客户端工作流程:

  1. 启动时:连接任意一个集群节点;

  2. 获取拓扑:发送 CLUSTER SLOTS,拉取完整的 slot → node 映射;

  3. 本地缓存:将映射表存入内存(如 Map<Integer, HostAndPort>);

  4. 执行命令时

    • 计算 slot = CRC16(key) % 16384
    • 查缓存,找到目标节点;
    • 直接向该节点发送命令

✅ 正常情况下,一次网络请求直达目标,无跳转、无延迟。


四、MOVED 重定向:客户端自我纠正,非服务端转发!

但缓存可能过期(如集群扩容、故障转移),客户端可能发错节点。

场景:

  • 客户端把 key="user:1001"(slot=12345)发给了 Node A;

  • Node A 负责 slots 0-5460,发现 slot 12345 不归自己管;

  • 于是返回:

    (error) MOVED 12345 192.168.1.11:7002
    

关键点来了:

Node A 不会把请求转发给 Node B!它只是告诉客户端:“去 192.168.1.11:7002 找”

客户端收到 MOVED 后:

  1. 解析出新地址;
  2. 更新本地缓存(slot 12345 → Node B);
  3. 自动重试命令,这次直连正确节点。

流程图:

客户端 → Node A(错误) → 返回 MOVED
客户端 → 更新缓存 → 重试 → Node B(正确) → 成功

🔥 Redis Cluster 故意不做服务端转发,原因有三:

  1. 避免级联延迟;
  2. 防止热点节点成为代理瓶颈;
  3. 保持节点职责单一(只处理自己负责的数据)。

五、特殊场景:ASK 重定向(迁移中)

当 slot 正在迁移(reshard)时,可能出现 ASK 错误:

(error) ASK 12345 192.168.1.11:7002

客户端处理方式:

  1. 先向目标节点发送 ASKING 命令(临时标记);
  2. 再发送原命令;
  3. 不更新本地缓存(因为迁移未完成,归属未定)。

⚠️ ASK 是临时状态,MOVED 才是永久变更。


六、Bonus:Hash Tag —— 强制多 key 落在同一 slot

Redis Cluster 默认不支持跨 slot 的多 key 操作。但你可以用 {} 包裹共同标识:

user:{1001}:name
user:{1001}:balance
order:{1001}:list

→ 只对 {1001} 做 CRC16 → 所有 key 落在同一 slot → 可安全用于 Lua 或事务。

💡 这是实现“聚合根”原子操作的唯一方式。


结语

Redis Cluster 的精妙之处在于:

  • 用确定性哈希保证数据分布可预测
  • 用 Gossip 协议实现去中心化一致性
  • 用客户端智能路由减少跳转开销
  • 用 MOVED/ASK 实现弹性容错