第五篇:主从复制与哨兵机制——Redis高可用的基石
前言在前面的文章中我们拆解了Redis的数据结构、内存管理和持久化。但还有一个根本性问题没有解决单机Redis存在单点故障——机器宕机整个服务不可用。这就是主从复制和哨兵机制要解决的问题。面试中这两个概念经常被连续追问“主从复制的全量复制和部分复制有什么区别”“哨兵是怎么发现主库故障的主观下线和客观下线有什么区别”“哨兵集群是怎么选主的”“哨兵和Cluster有什么区别”本文从主从复制的原理出发拆解哨兵机制的工作原理最后落到生产环境的高可用配置方案。本文核心问题主从复制的工作原理是什么全量复制和部分复制有什么区别主从延迟是怎么产生的如何处理哨兵是什么它是怎么发现主库故障的主观下线和客观下线有什么区别哨兵集群是怎么选主的选主逻辑是什么哨兵和Cluster各自的适用场景是什么秒杀项目中的Redis高可用是怎么配置的读完本文你将对Redis的高可用机制拥有从原理到配置的完整理解。一、主从复制——数据冗余的基础疑问为什么需要主从复制主库宕机了怎么办回答主从复制是Redis高可用的基础。主库负责写从库负责读。主库宕机后从库可以接管保证服务不中断。1.1 主从复制的基本架构┌──────────┐ 复制 ┌──────────┐ │ 主库 │─────────────→│ 从库 │ │ (写) │ │ (读) │ └──────────┘ └──────────┘ │ │ 复制 ↓ ┌──────────┐ │ 从库 │ │ (读) │ └──────────┘读写分离能降低主库压力——主库专注处理写操作读操作分摊到多个从库上。同时从库作为数据备份主库宕机时可快速切换到从库恢复服务。1.2 全量复制——初次建立连接从库第一次连接主库或长时间断开后重新连接时需要全量同步——主库执行bgsave生成RDB文件发送给从库从库清空自身数据后加载这个RDB再追加上同步期间的增量命令。全量复制流程 1. 从库向主库发送SYNC命令 2. 主库执行bgsave → 生成RDB快照 3. 主库将RDB文件发送给从库 4. 主库在发送RDB期间的写操作 → 记录到复制缓冲区 5. 从库接收RDB → 清空自身数据 → 加载RDB 6. 主库发送复制缓冲区中的增量命令 → 从库执行 7. 全量复制完成 → 进入增量复制阶段全量复制的开销主库bgsave消耗CPU和内存Copy-On-Write期间被修改的页要额外复制RDB文件传输占用网络带宽从库清空数据后加载RDB需要时间。大内存实例的全量同步可能持续数分钟期间主从之间没有实时数据同步。1.3 部分复制——断线重连Redis 2.8支持部分复制——从库短暂断开后重连如果断连期间的增量还在主库的复制缓冲区中只同步缺失的这一小段增量不需要全量复制。部分复制的条件 1. 从库记录了上次同步的主库run_id 2. 主库的run_id没有改变没有重启或切换 3. 从库请求的偏移量还在主库的复制缓冲区中 不满足任何一条 → 退化为全量复制部分复制的核心主库维护一个复制缓冲区默认1MB记录最近的写命令。从库重连时告诉主库我上次收到偏移量X主库检查X是否还在缓冲区中——如果在直接发送X之后的所有增量如果不在只能触发全量复制。1.4 主从延迟主从复制是异步的——主库处理完写操作后不等待从库确认就返回客户端。从库可能落后主库几十毫秒到几秒。产生原因主库并发写入从库单线程回放命令网络带宽不足时RDB传输拖慢同步从库硬件比主库差回放速度跟不上。处理方式关键业务下单、支付直接读主库可接受延迟的查询列表、详情走从库延迟超标时告警并自动切换读流量回主库。二、哨兵机制——自动故障转移疑问主库宕机了怎么自动切换到从库回答哨兵Sentinel就是干这件事的——它监控Redis节点的健康状态主库故障时自动执行故障转移。2.1 哨兵的四个核心职责职责做什么如何实现监控检查主从节点是否正常运行定期发送PING命令通知节点故障时通过Pub/Sub通知客户端发布订阅消息自动故障转移主库故障时选择从库升级为新主库选主算法多个哨兵协商配置提供者客户端询问哨兵获取当前主库地址哨兵返回最新主库IP2.2 主观下线 vs 客观下线主观下线单个哨兵判断某个节点不可达。当哨兵发送的PING在指定时间内没有收到有效回复时这个哨兵将节点标记为主观下线。但主观下线可能是网络分区导致的误判——如果只有这一个哨兵的网络出了问题主库实际还在正常运行。客观下线多个哨兵共同判断主库是否真的故障。一个哨兵发现主库主管下线后询问其他哨兵是否也认为主库不可达。当法定人数quorum的哨兵都确认后主库被标记为客观下线——确认主库真正故障触发故障转移。配置quorum 2至少2个哨兵同意 场景哨兵A发现主库不可达 → 主观下线 哨兵A询问哨兵B你觉得主库挂了吗 → 哨兵B回答是的 哨兵A询问哨兵C→ 哨兵C回答我的网络也连不上 2个哨兵确认 → 客观下线 → 触发故障转移2.3 哨兵集群的选主过程客观下线确认后哨兵集群用Raft算法选举出一个哨兵作为本次故障转移的leader。选主条件基于从库与主库的同步偏移量——最接近原主库的从库优先多个从库偏移量相同时优先级高的从库slave-priority值小优先优先级也相同时run_id字母序最小的从库优先。leader哨兵执行故障转移将选中的从库升级为主库向其他从库发送新主库在哪的指令通知客户端主库地址变更。三、哨兵 vs Cluster维度哨兵SentinelCluster数据分片不涉及一主多从模式下所有节点存完整数据数据按哈希槽分片分布到多个主节点故障转移自动哨兵协作选主后通知所有从库和客户端自动Gossip协议在集群内部确认故障并投票选主适用规模中小规模数据量可存入单机内存大规模单机无法容纳全部数据复杂度较低较高客户端需要处理MOVED和ASK重定向客户端要求需要知道哨兵地址或支持Sentinel感知需要支持Cluster协议或使用Proxy组件选择规则数据能存入单机内存读多写少→哨兵简单够用数据量超过单机内存或写入量大→Cluster水平扩展。四、秒杀项目中的配置# sentinel.conf sentinel monitor mymaster 127.0.0.1 6379 2 # 2个哨兵同意即可客观下线 sentinel down-after-milliseconds mymaster 5000 # 5秒不可达即主观下线 sentinel failover-timeout mymaster 10000 # 故障转移超时10秒 sentinel parallel-syncs mymaster 1 # 故障转移后一次只允许一个从库做全量复制选型考量秒杀系统的库存和缓存全存在Redis中但数据量在2GB以内单机能容纳。一主两从三名哨兵分布在三台服务器上解决了单点故障。不需要Cluster的分片能力也不需要处理MOVED重定向的客户端兼容性——哨兵方案刚好够用。故障演练的发现手动kill主库进程后哨兵在5秒主观下线quorum投票选主过程中共耗时约8秒恢复服务。这8秒的恢复空窗内所有请求被Sentinel限流组件兜底——返回系统繁忙不强行操作数据。故障转移完成后新主库接管写操作从库自动切换到新主库继续同步。五、面试中这样回答面试官“哨兵是怎么工作的”回答框架“哨兵有四个核心职责监控、通知、自动故障转移、配置提供。它定期PING主从节点发现主库不可达时标记为主观下线然后询问其他哨兵确认——达到quorum法定人数后标记为客观下线。多个哨兵用类似Raft的方式选举出leader由leader在主库的从库中选一个数据最新的升级为新主库通知其他从库和新主库建立复制关系并通知客户端更新连接地址。”面试官“主观下线和客观下线有什么区别”回答“主观下线是单个哨兵的判断——自己连不上主库。但可能是网络分区导致的误判。客观下线是多个哨兵协商的结果——quorum个哨兵都认为主库不可达。只有客观下线才会触发故障转移这是为了防止单哨兵误判导致不必要的切换、甚至出现两个主库同时接受写入的split-brain。”总结主从复制是数据冗余的基础全量复制用于首次同步部分复制用于断线重连。复制缓冲区的大小决定是否退化为全量复制主从延迟是异步复制的固有代价关键业务读主库非关键业务读从库。延迟超标时自动切回主库哨兵负责高可用主观下线是单哨兵判断客观下线是法定人数确认。只有客观下线才触发故障转移防止误判哨兵集群内部选举leader执行故障转移选主逻辑从数据完整性、配置优先级、run_id三个维度逐层判断哨兵 vs Cluster是规模和复杂度的选择数据量在单机内存内选哨兵超过单机内存选Cluster秒杀项目用哨兵方案数据量小、读写比例高、不需要分片的经验丰富度和容错特性。一主两从三哨兵主库宕机后几秒内自动恢复服务下一篇预告Redis原理六——Redis Cluster分布式缓存的进阶方案。拆解哈希槽的数据分片原理MOVED和ASK重定向的区别以及Cluster模式下的故障转移和局限性。