Skip to content

04.3 - Redis Cluster 架构

定位: Redis 的分布式原生方案——自动分片 + 数据高可用 + 去中心化 面试高频度: ⭐⭐⭐⭐⭐ 考查方式: 无中心设计、Gossip 协议、MOVED/ASK 重定向、故障检测


一、这是什么?为什么需要它?

是什么

Redis Cluster 是 Redis 原生的分布式方案,提供数据自动分片高可用能力,最小推荐部署 6 个节点(3 主 3 从)。

为什么需要 Cluster?

主从 + Sentinel 的限制:

  1. 数据总量不能超过单机内存 → 100GB 数据 → 买 256GB 内存机器
  2. 写能力受限于单机 CPU → 10 万 QPS 就是极限
  3. Sentinel 不处理数据分片 → 需要客户端做一致性哈希

Cluster 的解决:

  1. 数据分片:自动将 key 分配到 16384 个槽,每个节点管理一部分槽 → 容量线性扩展
  2. 写能力:N 个主节点 = N 倍写性能,3 主节点 → 30 万 QPS
  3. 去中心化:无 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:3

4.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 的对比
  • **哈希槽详解** — 重分片的数据迁移过程
  • ← 返回 **高可用与集群索引**

Knowledge4J — Java 知识库