集群数据分布定位
数据在 Redis Cluster 中如何存放
Redis Cluster 不使用传统的一致性哈希算法,而是采用哈希槽 + 虚拟槽映射表的方案。整个数据定位流程分为三步:计算哈希槽 → 查找槽归属 → 定位物理节点。
三步定位法
第一步:计算 Key 的哈希槽
slot_id = CRC16(key) % 16384
CRC16 是一种高效的循环冗余校验算法。Redis 对其做了特殊处理——如果 Key 中包含 {...} 格式的大括号,则只对大括号内的内容计算哈希(即 Hash Tag 机制)。
第二步:查找槽所属节点
每个节点都维护着一份集群状态信息,其中包含一个长度为 16384 的槽位数组(slots array),记录了每个槽由哪个节点负责。
slot 0 → 节点 A (node-id: abc123)
slot 1 → 节点 A
...
slot 5461 → 节点 B (node-id: def456)
...
slot 16383 → 节点 C (node-id: ghi789)
第三步:路由到目标节点
客户端侧路由(Smart Client):
大多数 Redis 客户端(如 JedisCluster、Lettuce、redis-py-cluster)实现了智能路由。它们:
1. 连接集群中的任意节点
2. 通过 CLUSTER SLOTS 或 CLUSTER NODES 命令获取完整的槽-节点映射表
3. 在本地缓存这份映射表
4. 直接计算 Key 的槽位,然后连接到目标节点
如果客户端缓存过时(比如发生了槽迁移),节点会返回 MOVED 错误,客户端更新缓存后重试。
槽位信息的传播
初始化阶段
当节点加入集群时:
1. 空节点加入集群(通过 CLUSTER MEET)
2. 主节点分配槽位(通过 CLUSTER ADDSLOTS)
3. 节点将自己的槽位信息广播给集群中其他节点
4. 其他节点更新本地槽位映射
运行阶段
节点之间通过 Gossip 协议定期交换信息。每个 PING/PONG 消息中都包含发送节点的槽位位图(2048 字节的 bit array),标记哪些槽属于自己。
节点内部数据结构
每个 Redis Cluster 节点内部维护着两个关键的槽位数组:
clusterState.slots[16384]
这是一个数组,每个元素指向一个 clusterNode 结构体。通过槽位号可以直接 O(1) 找到负责该槽的节点。
// 伪代码
clusterNode *slots[16384];
// slots[7256] = &nodeB; // 槽 7256 由节点 B 负责
clusterNode.slot_info[]
每个节点自身维护一个位图,记录自己负责哪些槽。这个位图在 Gossip 消息中传播。
MOVED 与 ASK 重定向
MOVED 重定向(永久)
-MOVED 7256 192.168.1.3:6379
含义:槽 7256 已经永久迁移到了 192.168.1.3:6379。客户端应该立即更新本地缓存,后续所有属于槽 7256 的 Key 都直接发往新节点。
ASK 重定向(临时)
-ASK 7256 192.168.1.3:6379
含义:槽 7256 正在迁移过程中,该 Key 可能已经迁移到了目标节点。客户端需要先向目标节点发 ASKING 命令,然后再发原命令。ASK 只是临时一次性行为,不会更新客户端本地缓存。
对比一致性哈希
| 特性 | 哈希槽方案 | 一致性哈希 |
|---|---|---|
| 虚拟节点数 | 固定 16384 | 可配置 |
| 数据分布均匀性 | 好(槽数远大于节点数) | 依赖虚拟节点配置 |
| 增删节点影响 | 只影响被迁移的槽 | 只影响相邻节点 |
| 实现复杂度 | 较简单 | 较复杂 |
| 客户端要求 | 必须支持智能路由 | 需要哈希环 |
总结
Redis Cluster 的数据定位采用了”计算槽→查映射→找节点“的三步方式。客户端通过缓存完整的槽位映射表实现高效路由,节点通过 Gossip 协议保持映射表的一致性。MOVED 和 ASK 两种重定向策略分别处理了稳态和迁移态的路由问题,保证了集群在动态变化中的数据可达性。


暂无评论内容