04.3 - Redis Cluster 架构
定位: Redis 的分布式原生方案——自动分片 + 数据高可用 + 去中心化 面试高频度: ⭐⭐⭐⭐⭐ 考查方式: 无中心设计、Gossip 协议、MOVED/ASK 重定向、故障检测
一、这是什么?为什么需要它?
是什么
Redis Cluster 是 Redis 原生的分布式方案,提供数据自动分片和高可用能力,最小推荐部署 6 个节点(3 主 3 从)。
为什么需要 Cluster?
主从 + Sentinel 的限制:
- 数据总量不能超过单机内存 → 100GB 数据 → 买 256GB 内存机器
- 写能力受限于单机 CPU → 10 万 QPS 就是极限
- Sentinel 不处理数据分片 → 需要客户端做一致性哈希
Cluster 的解决:
- 数据分片:自动将 key 分配到 16384 个槽,每个节点管理一部分槽 → 容量线性扩展
- 写能力:N 个主节点 = N 倍写性能,3 主节点 → 30 万 QPS
- 去中心化:无 Proxy,客户端直连节点(不像 Codis/Twemproxy 需要代理层)
二、原理拆解
2.1 无中心架构
Cluster 的优势:
- 无中心节点 → 无单点瓶颈
- 客户端直连 → 少一跳网络
- 线性扩展 → 加节点即可
- 客户端需支持 cluster 协议(智能客户端)
2.2 节点间通信(Gossip 协议)
Cluster 节点间使用 Gossip 协议交换信息: 每个节点维护一份 cluster_state(集群状态),包含:
- 所有节点的 ID、IP、Port
- 每个节点负责的哈希槽范围
- 每个节点的状态(online/fail)
- 主从关系
Gossip 消息类型:
- MEET: 新节点加入集群
- PING: 定期检查其他节点状态
- PONG: 响应 PING
- FAIL: 广播某个节点挂了的消息
Gossip 的优缺点:
- 去中心化、无单点
- 可扩展(每个节点只与少数通信)
- 信息传播有延迟(不是实时的)
- 网络开销(节点多时 PING 流量大)
2.3 客户端路由与 MOVED/ASK
客户端请求过程:
bash# 1. 计算哈希槽 slot = CRC16("user:1001") % 16384 → slot 1234 # 2. 查本地槽表 → Node B 负责 slot 1234 # 3. 直连 Node B 发送命令 # 4. 如果槽表过时:Node B 返回 MOVED,客户端更新槽表重定向
MOVED(槽已永久迁移):
ASK(槽正在迁移中):
- MOVED = 永久重定向(客户端应更新槽表)
- ASK = 临时重定向(槽正在迁移,不要更新槽表)
2.4 故障检测与容错
Cluster 与 Sentinel 的区别:
- Sentinel = 独立监控系统(3 个哨兵进程管理主从)
- Cluster = 自愈(集群节点自己管理自己)
三、图解全景
每个节点维护完整 cluster_state
四、实战验证
4.1 搭建最小集群
bash
# 创建 6 个节点配置(端口 7000-7005)
for port in 7000 7001 7002 7003 7004 7005; do
mkdir -p /tmp/redis-cluster/$port
cat > /tmp/redis-cluster/$port/redis.conf << EOF
port $port
cluster-enabled yes
cluster-config-file nodes-$port.conf
cluster-node-timeout 5000
appendonly yes
daemonize no
EOF
redis-server /tmp/redis-cluster/$port/redis.conf &
done
# 创建集群(使用 redis-cli --cluster 命令)
redis-cli --cluster create \
127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1
# → 3 主 3 从,每个主节点一个从节点
# 查看集群信息
redis-cli -p 7000 CLUSTER INFO
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_slots_ok:16384
# cluster_known_nodes:6
# cluster_size:34.2 验证分片
bash
# 计算 key 的 slot
redis-cli -p 7000 CLUSTER KEYSLOT "user:1001"
# (integer) 1234 ← 这个 key 属于 slot 1234
# 查看 slot 归属
redis-cli -p 7000 CLUSTER NODES
# abc123 127.0.0.1:7000@17000 master - 0 1234 0-5460
# def456 127.0.0.1:7001@17001 master - 0 1234 5461-10922
# 789ghi 127.0.0.1:7002@17002 master - 0 1234 10923-16383
# ...
# 验证跨节点写入
redis-cli -c -p 7000 # -c = cluster 模式(自动重定向)
127.0.0.1:7000> SET user:1001 "Alice"
-> Redirected to slot [1234] located at 127.0.0.1:7001 # 自动跳转
OK
127.0.0.1:7001> GET user:1001
"Alice"4.3 测试故障转移
bash
# 1. 模拟主节点宕机
redis-cli -p 7000 DEBUG SLEEP 30 # 让 7000 主节点暂停
# 2. 快速(几秒后)查看集群状态
redis-cli -p 7001 CLUSTER INFO | grep cluster_state
# cluster_state:ok # 从节点已自动升级
redis-cli -p 7001 CLUSTER NODES | grep master
# 可以看到 7003(原从节点)变成了 master
# 3. 恢复旧主节点(30 秒后)
# 它发现自己的主节点已被取代,自动成为从节点
redis-cli -p 7000 CLUSTER NODES | grep myself
# ... slave ... # 已变成从节点4.4 集群性能对比
bash
# 单机 Redis QPS
redis-benchmark -n 100000 -t SET -q
# 约 10 万 QPS
# 3 节点集群 QPS
redis-benchmark -n 100000 -t SET -q -p 7000 --cluster
# 约 20-30 万 QPS(取决于分布均匀度)
# 注意:pipeline + cluster 可获得更高吞吐五、面试视角
| 追问 | 答案要点 |
|---|---|
| Cluster 和 Sentinel 的区别? | Sentinel 只做主从切换,不负责分片;Cluster 自动分片 + 自愈,去中心化 |
| Cluster 的哈希槽数量为什么是 16384? | CRC16 输出 16 位(65536 个槽),但 16384 足够;消息大小更小(2KB vs 8KB) |
| MOVED 和 ASK 的区别? | MOVED 表示槽已永久迁移,客户端应更新槽表;ASK 表示槽正在迁移,不要更新槽表 |
| Gossip 协议的作用? | 节点间通过 PING/PONG 交换集群状态,每个节点每秒随机 Ping 5 个其他节点 |
| Cluster 的故障检测流程? | 节点超时 → PFAIL (可能挂) → Gossip 传播 → 多数确认 → FAIL |
| Cluster 的最小节点数? | 3 主 3 从(至少 3 个主节点投票才能保证 majority) |
| 客户端如何知道数据在哪? | 计算 CRC16(key) % 16384 → 查本地 slot 表 → 直连节点;MOVED 时更新槽表 |
📚 相关链接
- **主从复制** — Cluster 内部复制的原理
- **Sentinel** — Sentinel 与 Cluster 的对比
- **哈希槽详解** — 重分片的数据迁移过程
- ← 返回 **高可用与集群索引**