一、前言:为什么 Redis Cluster 不用一致性哈希?
很多分布式系统(如 Memcached)采用一致性哈希 实现数据分片,但 Redis Cluster 却另辟蹊径,选择了 "固定数量插槽 + 哈希取模" 的方案。
你是否曾疑惑:
- 为什么是 16384 个插槽,不是 1024 或 65536?
- key 是怎么决定去哪个节点的?
- 为什么
MGET key1 key2有时会报错?
本文将带你彻底搞懂 Redis 插槽(Slot)机制的设计思想与工作原理。
二、插槽:Redis Cluster 的分片单元
2.1 核心概念
Redis Cluster 将整个键空间划分为 16384 个逻辑插槽(slot) ,编号从 0 到 16383。
-
每个 key 通过公式映射到一个 slot:
slot = CRC16(key) % 16384 -
每个 master 节点负责一部分插槽(可以不连续)
-
客户端根据 slot 路由请求到对应节点
✅ 优势:
- 分片规则简单、确定
- 扩容时只需迁移部分 slot,无需 rehash 全量数据
- 节点增减对客户端透明(自动重定向)
三、为什么是 16384?深度解析设计权衡
这是 Redis 作者 antirez 在 GitHub 上亲自回答的经典问题。
3.1 官方解释(精简)
"16384 是在消息大小 、集群规模 和内存开销之间取得的最佳平衡。"
3.2 技术细节分析
| 插槽数 | 每节点心跳包大小 | 支持最大节点数 | 内存开销(每节点) |
|---|---|---|---|
| 65536 | ~130 KB | < 100 | 128 KB |
| 16384 | ~32 KB | ~1000 | 32 KB |
| 4096 | ~8 KB | > 1000 | 8 KB |
🔍 关键原因:
-
心跳包不能太大
Redis 节点每秒通过 gossip 协议广播集群状态,包含每个 slot 的归属。
若 slot 太多(如 65536),心跳包过大,浪费带宽。
-
slot 不能太少
若只有 1024 个 slot,当集群有 100 个节点时,平均每个节点仅 10 个 slot,负载极不均衡。
-
16384 = 2^14
- 足够大:支持千级节点(实际生产建议 ≤ 100)
- 足够小:心跳包控制在 32KB 以内
- 内存友好:每个 slot 状态占 2 字节 → 总计 32KB
💡 结论 :16384 不是魔法数字,而是工程实践中的最优解。
四、Key 到插槽的计算流程
4.1 标准计算公式
slot = CRC16(key) % 16384
4.2 特殊情况:Hash Tag(哈希标签)
Redis 支持通过 {} 强制多个 key 落在同一插槽:
user:{1001}:name→ 取{1001}计算 slotuser:{1001}:age→ 同样取{1001}→ 相同 slot
✅ 用途 :解决 multi-key 操作跨 slot 问题(如
MGET,SUNION)
4.3 Java 示例:计算 key 所属插槽
java
public static int calculateSlot(String key) {
// 1. 提取 Hash Tag
String hashTag = key;
int start = key.indexOf('{');
if (start != -1) {
int end = key.indexOf('}', start + 1);
if (end != -1 && end > start + 1) {
hashTag = key.substring(start + 1, end);
}
}
// 2. CRC16 计算(使用标准实现)
int crc = CRC16.crc16(hashTag.getBytes(StandardCharsets.UTF_8));
// 3. 取模
return crc % 16384;
}
五、客户端如何找到 key 所在节点?
5.1 首次访问:MOVED 重定向
-
客户端随机连一个节点(如 7001)
-
节点计算 key 的 slot,发现不在自己负责范围
-
返回错误:
MOVED 5461 192.168.1.10:7002 -
客户端缓存
<slot → node>映射,并重连 7002
5.2 扩容迁移中:ASK 重定向
当 slot 正在迁移:
- 源节点返回
ASK 5461 192.168.1.10:7003 - 客户端先发
ASKING命令,再发原命令
✅ Lettuce / Jedis Cluster 客户端自动处理这些重定向,业务代码无感知。
六、为什么多 key 操作会失败?
6.1 错误示例
bash
redis-cli -c MGET user:1 order:1
# (error) CROSSSLOT Keys in request don't hash to the same slot
6.2 原因
Redis Cluster 要求所有 key 必须在同一插槽,否则无法保证原子性。
6.3 解决方案:使用 Hash Tag
bash
# 强制落在同一插槽
MGET {user100}.profile {user100}.settings # ✅ 成功
📌 最佳实践:对需要批量操作的数据,使用相同 Hash Tag 前缀。
七、插槽迁移与集群扩缩容
7.1 扩容流程(新增 master)
- 新节点加入集群(
CLUSTER MEET) - 从现有节点迁移部分插槽到新节点
- 客户端通过
MOVED/ASK自动重定向 - 迁移完成后,集群负载更均衡
7.2 迁移命令(底层)
bash
# 源节点执行:迁移 key
MIGRATE 192.168.1.11 7004 "" 0 5000 KEYS key1 key2
# 更新插槽归属
CLUSTER SETSLOT 5461 NODE <new-node-id>
⚠️ 注意 :迁移过程无需停机 ,客户端可能短暂收到
ASK。
八、常用运维命令
| 命令 | 作用 |
|---|---|
CLUSTER SLOTS |
查看所有插槽分配情况 |
CLUSTER KEYSLOT key |
查询 key 所属插槽 |
CLUSTER COUNTKEYSINSLOT 5461 |
统计某插槽 key 数量 |
CLUSTER GETKEYSINSLOT 5461 10 |
获取某插槽的 10 个 key |
九、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!