一、前言:为什么 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} 计算 slot
- user:{1001}:age → 同样取 {1001} → 相同 slot
✅ 用途:解决 multi-key 操作跨 slot 问题(如 MGET, SUNION)
4.3 Java 示例:计算 key 所属插槽
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 重定向
MOVED 5461 192.168.1.10:7002
5.2 扩容迁移中:ASK 重定向
当 slot 正在迁移:
- 源节点返回 ASK 5461 192.168.1.10:7003
- 客户端先发 ASKING 命令,再发原命令
✅ Lettuce / Jedis Cluster 客户端自动处理这些重定向,业务代码无感知。
六、为什么多 key 操作会失败?
6.1 错误示例
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
# 强制落在同一插槽
MGET {user100}.profile {user100}.settings # ✅ 成功
📌 最佳实践:对需要批量操作的数据,使用相同 Hash Tag 前缀。
七、插槽迁移与集群扩缩容
7.1 扩容流程(新增 master)
7.2 迁移命令(底层)
# 源节点执行:迁移 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 |
九、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!







