在 Redis 集群中,键的定位是通过 哈希槽(Hash Slot) 机制实现的。以下是完整的定位流程和关键设计点:
1. 哈希槽(Hash Slot)的基本概念
- 哈希槽数量:Redis 集群将键空间划分为 16384 个哈希槽(0~16383)。
- 键与槽的映射:每个键通过 CRC16 算法计算哈希值,并对 16384 取模,确定所属的槽:
hash_slot = CRC16(key) % 16384
例如,键 user:1000
的 CRC16 哈希值为 12345
,则其所属槽为 12345 % 16384 = 12345
。
2. 槽到节点的映射
- 节点负责槽:集群中的每个节点负责一部分哈希槽。例如:
- 节点 A:槽 0~5460
- 节点 B:槽 5461~10922
- 节点 C:槽 10923~16383
- 槽分布动态调整:当集群扩容或缩容时,槽会重新分配(通过
CLUSTER REBALANCE
命令),节点之间通过 Gossip 协议同步槽的归属信息。
3. 客户端如何定位键所在的节点
(1) 客户端视角的流程
- 计算哈希槽:客户端根据键名计算哈希槽。
- 查询槽映射表:客户端维护一个本地的 槽到节点的映射表(通过
CLUSTER SLOTS
命令获取或缓存 MOVED 错误更新)。 - 发送请求到对应节点:根据槽的归属节点,将请求发送到目标节点。
(2) 客户端处理重定向
- MOVED 错误:如果客户端发送请求到错误的节点,目标节点会返回
MOVED <slot> <ip:port>
错误,告知正确的节点地址。客户端更新本地映射表后重试。 - ASK 错误:在槽迁移过程中,如果键尚未迁移到新节点,源节点会返回
ASK <slot> <ip:port>
,客户端临时重定向到新节点(无需更新映射表)。
(3) 示例代码(伪代码)
def get_node(key):
slot = crc16(key) % 16384
if slot in local_slot_map:
return local_slot_map[slot]
else:
# 向任意节点发送请求,获取最新槽映射表
update_local_slot_map()
return local_slot_map[slot]
4. 关键设计点
(1) 为什么使用 16384 个槽?
- 平衡性:16384 个槽足够细粒度地分配数据,同时避免槽数量过多导致心跳包过大(Redis 集群节点数通常不超过 1000 个)。
- 兼容性:CRC16 的哈希值范围是 0~65535,取模 16384 可均匀分布。
(2) 哈希标签(Hash Tags)
- 场景:某些业务需要多个键存储在同一个节点(如订单
order:1000
和payment:1000
)。 - 实现:通过
{...}
包裹部分键名,强制使用括号内的子串计算哈希槽:
{user:1000}.profile 和 {user:1000}.orders -> 使用 "user:1000" 计算槽
(3) 客户端缓存优化
- 本地缓存:客户端缓存槽到节点的映射表,减少对集群的查询开销。
- 自动更新:当遇到
MOVED
或ASK
错误时,客户端主动更新缓存。
5. 集群节点的通信机制
- Gossip 协议:节点之间通过 Gossip 协议同步集群状态(如槽分配、节点存活状态),确保所有节点对集群拓扑有一致视图。
- 心跳包:每个节点周期性发送 PING/PONG 消息,传递自身负责的槽信息和集群元数据。
6. 典型场景分析
(1) 正常读写流程
- 客户端计算键
user:123
的槽为5000
。 - 根据本地映射表,槽
5000
由节点 B 负责。 - 客户端直接向节点 B 发送
GET user:123
请求。
(2) 槽迁移中的临时状态
- 节点 A 正在将槽
5000
迁移到节点 B。 - 客户端向节点 A 发送请求时,若键
user:123
已迁移,节点 A 返回ASK 5000 <B的地址>
。 - 客户端临时向节点 B 发送请求,无需更新本地映射表。
7. 与传统一致性哈希的对比
特性 | Redis 哈希槽 | 传统一致性哈希 |
---|---|---|
槽数量固定 | 16384 个槽,便于管理和分配 | 动态哈希环,节点增减需重新计算 |
数据迁移粒度 | 以槽为单位迁移数据 | 以键为单位迁移数据 |
客户端缓存 | 显式缓存槽到节点的映射表 | 依赖客户端自行维护哈希环 |
扩展性 | 槽分配灵活,适合大规模集群 | 节点增减可能导致大量键迁移 |
总结
Redis 集群通过 哈希槽 + 客户端缓存 + Gossip 协议 实现键的高效定位。其设计核心在于:
- 解耦键与节点:通过固定槽数量和动态槽分配,实现数据的灵活分布。
- 降低客户端复杂度:客户端只需维护槽映射表,无需感知集群拓扑变化。
- 高可用性:槽迁移和故障转移通过 Gossip 协议自动完成,保障服务连续性。
THE END