第五篇:主从复制与哨兵机制——Redis高可用的基石

前言

在前面的文章中,我们拆解了Redis的数据结构、内存管理和持久化。但还有一个根本性问题没有解决:单机Redis存在单点故障------机器宕机,整个服务不可用。

这就是主从复制和哨兵机制要解决的问题。面试中,这两个概念经常被连续追问:

"主从复制的全量复制和部分复制有什么区别?"

"哨兵是怎么发现主库故障的?主观下线和客观下线有什么区别?"

"哨兵集群是怎么选主的?"

"哨兵和Cluster有什么区别?"

本文从主从复制的原理出发,拆解哨兵机制的工作原理,最后落到生产环境的高可用配置方案。

本文核心问题:

  1. 主从复制的工作原理是什么?全量复制和部分复制有什么区别?
  2. 主从延迟是怎么产生的?如何处理?
  3. 哨兵是什么?它是怎么发现主库故障的?
  4. 主观下线和客观下线有什么区别?
  5. 哨兵集群是怎么选主的?选主逻辑是什么?
  6. 哨兵和Cluster各自的适用场景是什么?
  7. 秒杀项目中的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通知客户端 发布订阅消息
自动故障转移 主库故障时选择从库升级为新主库 选主算法(多个哨兵协商)
配置提供者 客户端询问哨兵获取当前主库地址 哨兵返回最新主库IP

2.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

维度 哨兵(Sentinel) Cluster
数据分片 不涉及(一主多从模式下,所有节点存完整数据) 数据按哈希槽分片分布到多个主节点
故障转移 自动,哨兵协作选主后通知所有从库和客户端 自动,Gossip协议在集群内部确认故障并投票选主
适用规模 中小规模,数据量可存入单机内存 大规模,单机无法容纳全部数据
复杂度 较低 较高,客户端需要处理MOVED和ASK重定向
客户端要求 需要知道哨兵地址或支持Sentinel感知 需要支持Cluster协议或使用Proxy组件

选择规则:数据能存入单机内存+读多写少→哨兵,简单够用;数据量超过单机内存或写入量大→Cluster,水平扩展。


四、秒杀项目中的配置

conf 复制代码
# 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模式下的故障转移和局限性。

相关推荐
卷毛的技术笔记3 小时前
双十一零点扛过10倍流量洪峰:Sentinel与Redis+Lua的分布式限流深度避坑指南
java·redis·分布式·后端·系统架构·sentinel·lua
Devin~Y4 小时前
大厂Java面试实录:Spring Boot/JPA/Redis/Kafka/K8s 可观测性 + Spring AI RAG/Agent(小Y翻车现场)
java·spring boot·redis·mybatis·hibernate·spring mvc·jpa
庞轩px4 小时前
第六篇:Redis Cluster——分布式缓存的进阶方案
redis·分布式·缓存
phltxy4 小时前
Redis:从入门到精通的第一步
数据库·redis·缓存
@Ma4 小时前
详细解读Redis作者antirez开源的ds4项目,ds4.c — DeepSeek V4 Flash 本地推理引擎(中文文档)
c语言·redis·开源
工业甲酰苯胺18 小时前
Redis--集群搭建与主从复制原理
数据库·redis·php
人道领域20 小时前
【黑马点评日记】:用户签到功能详解——从Bitmap入门到避坑指南
java·数据库·redis·后端
庞轩px1 天前
第五篇:分布式锁实战——Lua脚本原子操作与库存扣减的强一致性
redis·lua·分布式锁·synchronized·原子性·零超卖
直奔標竿1 天前
MySQL与Redis数据一致性实战方案(避坑指南)
java·数据库·spring boot·redis·mysql·spring·缓存