公司动态

08-Redis 高可用架构:主从、哨兵、集群原理

📅 2026/8/18 2:08:16
08-Redis 高可用架构:主从、哨兵、集群原理
Redis 高可用架构主从、哨兵、集群原理作者黒漂技术佬 | 系列Redis 缓存与高并发实战生产环境里Redis 单机部署就是定时炸弹——机器一挂缓存全没数据库瞬间被流量打穿。高可用不是可选项是必选项。Redis 提供了三种高可用方案主从复制、哨兵、集群各有适用场景。一、Redis 单机的问题单机 Redis 面临两大痛点单点故障一台机器挂了整个缓存层不可用。没有备份数据全丢。性能瓶颈单机 Redis 再快也有物理上限。单实例 QPS 大约 10 万左右如果你的业务量到 50 万 QPS一台扛不住。单机架构 Client → [Redis 单机] ← 挂了就全完解决思路也很直白加机器。一台不行就多台一个挂了还有另一个顶上。这就是高可用架构的核心——冗余 自动故障转移。二、主从复制Master-Slave基本架构一主多从Master 负责写Slave 负责读。Master 写入数据后自动同步到 Slave。┌──────────┐ │ Master │ ← 读写通常只写 │ (Redis-1) │ └─────┬─────┘ │ 复制 ┌────────┼────────┐ ↓ ↓ ↓ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Slave-1 │ │ Slave-2 │ │ Slave-3 │ ← 只读 └─────────┘ └─────────┘ └─────────┘读写分离主从架构最直接的价值是读写分离写操作SET/DEL/INCR 等走 Master读操作GET/HGET/ZRANGE 等走 Slave读请求被多个 Slave 分摊整体吞吐量提升适用场景读多写少的业务。比如无人售货柜——商品查询读远多于库存修改写一主两从就能把读 QPS 分摊到三台机器。全量同步Slave 第一次连接 Master 时需要把 Master 的全部数据同步过来这个过程叫全量同步1. Slave 发送 PSYNC 命令给 Master 2. Master 执行 BGSAVE生成 RDB 快照文件 3. Master 把 RDB 文件发给 Slave 4. Slave 加载 RDB恢复数据 5. Master 把生成 RDB 期间的增量写命令发给 SlaveSlave: 我是新来的给我全量数据 → Master: BGSAVE 生成快照 → Master: 发送 RDB 文件 → Slave: 加载 RDB → Master: 发送期间的增量命令 → Slave: 执行增量命令同步完成注意BGSAVE 会 fork 子进程大内存实例 fork 可能导致短暂阻塞。如果 Slave 很多全量同步会消耗大量网络带宽和 Master CPU。增量同步全量同步之后Master 每收到写命令都会异步同步给 SlaveClient → Master: SET key value Master → Slave: SET key value (异步复制)增量同步基于replication offset复制偏移量Master 维护一个全局偏移量每写入一条命令就 1Slave 也维护自己的偏移量记录已同步到的位置如果 Slave 断线重连Master 根据 offset 只补发缺失的部分Master offset: 10000 ↓ Slave 断线时 offset: 8000 ↓ Slave 重连 ↓ Master 发现 offset 差距 2000 ↓ 补发 8001~10000 之间的命令增量同步 如果差距太大超过了复制积压缓冲区大小就只能全量同步了主从延迟Redis 的主从复制是异步的——Master 写完立即返回客户端不等 Slave 确认。这带来一个问题主从延迟。Master: SET stock 99 → 立即返回 OK Slave: 还没收到同步 → GET stock 返回 100旧值延迟通常在毫秒级但在高写入或网络抖动时可能变大。常见应对方案读 Master对一致性敏感的读操作直接读 Master 而非 Slave乐观读读 Slave 后校验 offset如果 Slave 落后太多就读 Master主从复制的局限主从复制解决了性能瓶颈读写分离分摊压力但没解决自动故障转移Master 挂了 → 谁来顶上 → 需要人工介入半夜三更爬起来手动切换 → 不可接受需要自动化 → 哨兵登场三、哨兵Sentinel哨兵是做什么的哨兵Sentinel是 Redis 官方提供的高可用组件核心功能就一句话Master 挂了自动选一个 Slave 升级为 Master不需要人工干预。┌─────────────┐ │ Sentinel-1 │ ──┐ │ Sentinel-2 │ ──┤ 哨兵集群监控 决策 │ Sentinel-3 │ ──┘ └──────┬──────┘ │ 监控 ┌──────┼──────┐ ↓ ↓ ↓ Master Slave-1 Slave-2哨兵的三大职责监控持续检测 Master 和 Slave 是否存活通知实例出问题时通知运维或客户端自动故障转移Master 挂了自动选 Slave 顶上主观下线 vs 客观下线哨兵判断 Master 是否挂了分两步主观下线Subjectively Down, SDOWN单个哨兵发现 Master 没在指定时间down-after-milliseconds内响应 PING就标记它主观下线。但这可能只是网络抖动——也许只是这个哨兵和 Master 之间的网络有问题。Sentinel-1: 我 ping 不到 Master认为它挂了 → 主观下线 Sentinel-2: 我能 ping 到Master 没问题 Sentinel-3: 我也能 ping 到客观下线Objectively Down, ODOWN当过半数quorum 配置通常 半数哨兵都认为主观下线才标记为客观下线——这回真挂了准备故障转移。Sentinel-1: 主观下线 Sentinel-2: 主观下线 Sentinel-3: 正常 2/3 认为下线 → 达到 quorum → 客观下线 → 启动故障转移为什么两步判断避免误判。单个哨兵的网络问题不应触发故障转移多哨兵交叉验证才可靠。哨兵选举 Leader 流程故障转移需要一个 Leader 来执行。选举过程基于Raft 算法发现 Master 客观下线的哨兵先投自己一票发起选举请求其他哨兵收到请求后如果还没投过票就同意投它一票第一个获得过半选票的哨兵成为 LeaderLeader 负责执行故障转移Sentinel-1: 我要当 Leader → 向 Sentinel-2, Sentinel-3 发起请求 Sentinel-2: OK投你 → Sentinel-1 票数 2/3 Sentinel-3: OK投你 → Sentinel-1 票数 3/3 → 当选 Leader故障转移过程Leader 哨兵执行以下步骤选最优 Slave根据优先级slave-priority、复制偏移量数据最新、runid最小选一个 Slave升级 Slave 为 Master发送SLAVEOF NO ONE命令通知其他 Slave让其他 Slave 指向新 Master 复制数据通知客户端客户端连接哨兵获取新 Master 地址故障前 Master(A) ← Slave(B), Slave(C) Master A 挂了故障转移后 Master(B) ← Slave(C) A 如果恢复会变成 B 的 Slave为什么至少 3 个哨兵哨兵自身也需要高可用。2 个哨兵的话一个挂了就达不到 quorum无法做决策。3 个挂 1 个还能工作2/3 达到多数。一般部署奇数个哨兵3 或 5 个。四、集群Cluster为什么需要集群主从和哨兵解决了高可用但有个问题没解决单机容量瓶颈。一台 Redis 机器最多几十 GB 内存。如果你有 500GB 数据单机存不下。而且单 Master 的写入能力也有上限。Redis Cluster 通过数据分片解决这个问题——把数据分散到多个 Master 节点上每个节点只存一部分数据。Redis Cluster 架构 ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ Master-1 │ │ Master-2 │ │ Master-3 │ │ 槽 0~5460 │ │ 槽 5461~10922 │ │ 槽 10923~16383 │ └────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ │ │ │ ┌────────┴─────────┐ ┌────────┴─────────┐ ┌────────┴─────────┐ │ Slave-1 │ │ Slave-2 │ │ Slave-3 │ └──────────────────┘ └──────────────────┘ └──────────────────┘16384 个哈希槽Redis Cluster 把所有 Key 映射到0~16383共 16384 个哈希槽Hash Slot中。每个 Master 节点负责一部分槽。计算 Key 属于哪个槽 slot CRC16(key) mod 16384 例3 个 Master 节点 Master-1: 槽 0 ~ 5460 (5461 个槽) Master-2: 槽 5461 ~ 10922 (5462 个槽) Master-3: 槽 10923 ~ 16383 (5461 个槽)为什么是 16384Redis 作者 antirez 在 GitHub 上回答过16384 2^14压缩后每个节点的槽位信息只需 2KB16384/8。如果用 65536 个槽每个节点需要 8KB。而且 Redis Cluster 建议最多 1000 个节点16384 个槽绰绰有余。客户端读写时先计算 Key 的哈希槽找到对应的 Master 节点然后直接发请求Client: SET product:1001 可乐 → CRC16(product:1001) mod 16384 5498 → 5498 在 Master-2 的范围内 → 发送到 Master-2 执行如果客户端把请求发到了错误的节点节点会返回MOVED重定向Client → Master-1: SET product:1001 可乐 Master-1 → Client: MOVED 5498 Master-2的IP:port Client → Master-2: SET product:1001 可乐 ← 重新发送到正确节点智能客户端如 Jedis Cluster、Lettuce会在本地缓存槽位映射表大部分请求不需要重定向。节点通信Gossip 协议Cluster 中各节点怎么知道彼此的状态答案Gossip 协议。Gossip八卦协议的工作方式像传八卦——每个节点定期随机选几个节点把自己知道的信息告诉对方对方再传给其他节点最终所有节点信息一致。每个节点每秒做这些事 1. 随机选 5 个节点发送 PING 消息 2. PING 消息包含自己知道的所有节点状态部分 3. 收到 PING 的节点回复 PONG也带上自己知道的信息 4. 信息像八卦一样扩散开最终所有节点信息趋同Gossip 通信的内容包括节点是否在线槽位分配情况主从关系Gossip 的特点去中心化没有统一的协调者、最终一致性信息传播需要时间不是立即一致、可扩展节点数增加不影响通信效率。集群扩缩容扩容加节点1. 新节点加入集群 → redis-cli --cluster add-node new-node:7004 existing-node:7000 2. 迁移哈希槽 → 从现有节点迁移部分槽到新节点 → 例从 Master-1 迁 1000 个槽到 Master-4 → 迁移过程中涉及到的 Key 用 ASK 重定向临时 → 迁移完成后更新槽位映射 3. 给新节点配 Slave可选缩容减节点1. 把要下线节点的槽迁移到其他节点 2. 从集群中移除该节点 3. 关闭节点槽迁移期间不影响服务迁移中的 Key 通过 ASK 重定向处理——客户端收到 ASK 后临时跳转到目标节点查询迁移完成后恢复 MOVED 正常重定向。集群模式的限制只支持单 Key 原子操作跨槽的多 Key 操作如 MGET、事务不被直接支持。需要用 Hash Tag 把相关 Key 分到同一槽。不支持跨库Cluster 模式只有一个 db0。批量操作限制KEYS *只返回当前节点的 Key不是全集群的。Hash Tag 用法# 用 {} 括起来的部分参与哈希计算SET{user:1001}:profile...# 槽 CRC16(user:1001) mod 16384SET{user:1001}:orders...# 同上和 profile 在同一槽# 这样就能对这两个 Key 做事务操作五、三种方案对比维度主从复制哨兵 Sentinel集群 Cluster数据分片不支持不支持支持16384 槽高可用不支持自动切换支持自动故障转移支持自动故障转移读写分离支持支持支持读 Slave容量上限单机内存单机内存多机内存之和写入上限单 Master单 Master多 Master 分摊复杂度低中高客户端普通哨兵客户端Cluster 客户端最少节点21主1从3哨兵1主1从63主3从选型建议主从复制数据量不大读 QPS 需要分摊有运维团队可以手动处理故障适合中小规模项目哨兵数据量不大单机能存下但需要自动故障转移追求高可用但不想搞太复杂适合大多数中小型生产项目集群数据量超过单机内存写入 QPS 超过单机上限需要水平扩展适合大型项目生产环境 Redis 高可用选型建议以无人售货柜场景为例小型部署100台以下设备 → 哨兵模式1主2从 3哨兵 → 数据量小自动故障转移够用 中型部署100~1000台设备 → 哨兵模式或小规模集群3主3从 → 取决于数据量和写入压力 大型部署1000台以上设备 → Redis Cluster6主6从或更大 → 库存、订单、设备状态数据量大需要分片实用建议如果你的数据量在 32GB 以内哨兵模式基本够用运维成本最低。超过 32GB 或写入 QPS 超过 10 万上集群。别为了看起来高级就上 Cluster——它的运维复杂度和客户端限制是实打实的成本。六、总结一张图Redis 高可用演进 单机 → 主从复制 → 哨兵 → 集群 ───── ────────── ────── ───── 单点故障 ──→ 读写分离 ──→ 自动故障转移 ──→ 数据分片 性能瓶颈 ──→ 性能提升 ──→ 高可用 ──→ 水平扩展 无自动切换 单机容量限制 运维复杂三种方案不是互斥的——哨兵模式本身就需要主从复制集群模式每个分片也是主从结构。它们是递进关系根据业务规模选择合适的方案。