2026年7月12日 · 6 分钟阅读

Redis 高可用架构:主从复制、哨兵与分片集群

深入 Redis 主从同步的 replid/offset 机制、哨兵的主观/客观下线判定与故障转移流程、分片集群的 16384 个哈希槽路由原理与集群通信模型。

单机 Redis 有三个瓶颈:单点故障、海量数据存不下、写请求扛不住

围绕这三个问题,Redis 提供了三层高可用方案:

主从复制 → 哨兵 → 分片集群

每一层解决不同的痛点。从底层原理看,它们不是三个独立的机制,而是一层一层叠加的。


主从复制

为什么需要主从

  • 高可用:一台 Redis 挂了,还有另一台接着提供服务。
  • 读写分离:master 扛写,slave 扛读,提升吞吐量。
  • 数据备份:在 slave 上做持久化,不影响 master。

全量同步

slave 第一次连接到 master 时,触发全量同步

slave → slaveof master_ip master_port
     → master 执行 bgsave 生成 RDB 快照
     → master 将 RDB 文件发送给 slave
     → slave 清空自身数据,加载 RDB
     → master 将 RDB 生成期间的增量写命令(缓冲区)发送给 slave
     → slave 执行增量命令

replid 和 offset

全量同步之后,怎么知道后续是否需要同步?

Redis 主从之间通过两个标识来判断数据一致性:

  • replication ID(replid):数据集的”姓氏”(标记)。每个 master 有唯一的 replid,slave 继承 master 的 replid。
  • offset:偏移量。每条写入命令在复制积压缓冲区(repl_backlog)中都有位置。
if slave.replid == master.replid and slave.offset < master.offset:
    # 可以增量同步
    send_commands(master.repl_backlog[slave.offset : master.offset])
else:
    # replid 变了,或者 offset 对不上 → 全量重同步
    full_sync()

增量同步

全量同步之后,master 的写命令会同时写入repl_backlog(一个固定大小的环形缓冲区)。slave 定期把自己的 offset 发给 master,master 把 backlog 中缺失的部分发给 slave。

repl_backlog 大小默认 1MB。如果 slave 断连太久,offset 指向的数据在 backlog 中被覆盖了,就只能再次全量同步。

优化建议

  • 启用无磁盘复制:repl-diskless-sync yes,master 直接将 RDB 数据通过 socket 发给 slave,避免磁盘 IO。
  • 单节点内存不要太大(≤ 8G),减少 fork 和 RDB 传输压力。
  • 适当增大 repl-backlog-size,让 slave 有更长的断连容忍时间。
  • 主-从-从链式结构:一个 master 挂太多 slave 会消耗大量网络带宽,形成级联。

哨兵(Sentinel)

主从复制解决了”数据有备份”,但没解决”master 挂了怎么办”。哨兵就是用来做自动故障转移的。

三个职责

  • 监控(Monitoring):每秒向所有节点发送 ping,检查是否存活。
  • 自动故障转移(Failover):master 宕机时,选择一个 slave 提升为 master。
  • 通知(Notification):将最新主从信息推送给客户端。

心跳与下线判定

哨兵集群之间独立决策,通过投票达成共识:

  • 主观下线(SDOWN):单个哨兵发现一个实例在规定时间内没响应 ping。
  • 客观下线(ODOWN):超过 quorum 数量的哨兵都认为该实例主观下线,才判定客观下线。

quorum 建议超过哨兵总数的一半(例如 3 个哨兵设 quorum = 2)。

故障转移流程

当 master 被判定客观下线:

1. 哨兵集群选出一个 leader 执行故障转移
2. leader 选择新的 master(选优规则如下)
3. 向备选 slave 发送 slaveof no one,提升为 master
4. 向其他 slave 发送 slaveof new_master_ip new_master_port
5. 将原 master 标记为 slave,恢复后自动成为新 master 的从节点

新 master 选择规则

1. 排除断线超过 down-after-milliseconds × 10 的 slave
2. 选择 slave-priority 最小的(0 表示永不参与选举)
3. 若优先级相同,选 offset 最大的(数据最新)
4. 若 offset 也相同,选 run_id 最小的

客户端读取策略

策略说明
MASTER只从主节点读
MASTER_PREFERRED优先主节点,主不可用时读从
REPLICA只从从节点读
REPLICA_PREFERRED优先从节点,从不可用时读主(推荐)

分片集群

主从 + 哨兵解决了高可用和高并发读,但海量数据存储和高并发写这两个问题还没解决——你再怎么加 slave,单个 master 的内存和写能力是有上限的。

分片集群(Redis Cluster)就是要解决这两个问题。

哈希槽(Hash Slot)

Redis Cluster 把整个 key 空间划分为 16384 个哈希槽

slot = CRC16(key) % 16384

这 16384 个槽被平均分配给集群中的 master 节点

3 个 master:每个节点 ≈ 5461 个槽
6 个 master:每个节点 ≈ 2730 个槽

key 不是直接绑定到节点,而是绑定到槽。所以新增或删除节点时,只需要迁移槽(连带该槽中的 key),不需要整体 rehash。

分片路由

客户端请求任意节点:
  → 节点计算 key 的 slot
  → 如果 slot 在自己名下,直接处理
  → 否则返回 MOVED 错误,告诉客户端正确的节点

Redis 客户端(如 JedisCluster、Lettuce)会在本地缓存槽到节点的映射,尽量避免重定向。

请求合并

如果批量操作的 key 不在同一个 slot 上,Redis Cluster 会直接报错。这是为什么集群下批量操作要使用 hash tag来保证 key 落在同一槽:

user:{123}:name    → slot = CRC16("123") % 16384
user:{123}:email   → slot = CRC16("123") % 16384  // 同一个槽!

hash tag 规则:key 中第一个 {} 包含的内容作为有效部分计算 slot。

节点间通信

集群中的 master 之间通过 Gossip 协议 相互 ping 来检测健康状态。每次 ping 至少包含:

  • 发送者的槽信息
  • 发送者视角的集群状态
  • 其他节点的信息(随机的 2-3 个)

一个 10 节点的集群,每次 ping 携带的信息大约 1KB。节点数越多,集群内网络开销越大。建议集群节点数不超过 1000

手动故障转移

CLUSTER FAILOVER 命令让 slave 触发手动切换:

1. slave 向 master 发起同步请求,确保数据一致
2. master 停止处理写入,将复制偏移量同步给 slave
3. slave 完成同步,发起故障转移
4. 集群感知新 master 上线

支持三种模式:

模式说明
缺省校验 offset 一致性,确保数据不丢
force跳过 offset 校验
takeover直接接管,忽略所有一致性检查(极端情况)

节点配置建议

  • 节点数量不要超过 1000(gossip 协议流量随节点数平方级增长)。
  • 避免在同一物理机上运行过多 Redis 实例。
  • 配置合适的 cluster-node-timeout(默认 15000ms):节点超时判定时间,影响故障转移速度。
  • cluster-require-full-coverage yes(默认):如果有任意 slot 不可用,整个集群停止服务。生产环境建议根据业务容忍度决定是否改 no

三种方案对比

对比维度主从复制哨兵分片集群
解决的核心问题数据备份、读性能自动故障切换海量数据存储、高并发写
数据分片不支持不支持支持(16384 槽)
自动故障转移不支持(手动切换)支持支持
复杂度
节点数上限少量少量≤ 1000

使用场景建议:

  • 数据量小需要高可用 → 主从 + 哨兵。
  • 数据量大需要水平扩展 → 分片集群。
  • 读多写少且能容忍写性能瓶颈 → 主从 + 哨兵 + 读写分离。
  • 写频繁且数据量大 → 分片集群 + 每个分片再配置哨兵或主从。

总结

  • replid + offset 是主从同步的”身份证”,决定了全量还是增量同步。
  • 主观下线 + 客观下线 是哨兵的共识机制,quorum 决定了容错边界。
  • 16384 个哈希槽 是分片集群的路由基础,槽绑定数据而非绑定节点,让扩缩容的迁移量可控。
  • Redis Cluster 的 gossip 协议在节点数较少时工作良好,但超过 1000 个节点后性能开始下降——你需要拆分多个集群。