主从与哨兵
校招考察频度:☆ 中频(重点常考·大厂分水岭) 复习主线:高可用的第一层是主从复制(数据冗余与读写分离),第二层是哨兵 Sentinel(自动故障转移与拓扑变更通知)。面试高频主线:全量同步 vs 增量同步底层全流程、哨兵主观/客观下线与 Raft 选主机制、网络分区下的脑裂(Split-Brain)成因与参数级防御。
一、主从复制
主从复制拓扑
1. 同步过程(PSYNC 2.0 机制)
- 增量复制优先:从库连接主库发送
PSYNC <runid> <offset>,携带自身记录的主库 RunID 与复制偏移量。 - 判断是否能增量:
- 若从库 RunID 与当前主库一致,且
offset依然落在主库的环形复制积压缓冲区(repl_backlog_buffer)中,主库直接发送CONTINUE,从缓冲区中把落后的命令增量补发给从库。
- 若从库 RunID 与当前主库一致,且
- 全量复制兜底:
- 若首次连接(无 RunID)或网络中断时间过长导致从库 offset 已经被积压缓冲区覆盖(Backlog 溢出),主库触发全量同步:
- 主库后台
bgsave生成 RDB 快照并发送给从库,同时将期间新产生的所有写命令缓存到复制缓冲区(Replication Buffer)。 - 从库清空本地旧数据、加载 RDB 文件,加载完毕后主库再将 Replication Buffer 增量命令刷给从库。
- 核心注意点:
- 复制是纯异步的:主库写入内存成功即向客户端返回 ACK,从库延迟毫秒到秒级不等。
- 全量同步成本高昂:大实例生成和传输 RDB 严重占用磁盘 I/O 和网卡带宽,从库加载 RDB 期间会阻塞读请求。
二、哨兵(Sentinel):自动故障转移
哨兵集群解决“主库宕机后需要人工介入切主”的痛点。三大职责:
| 职责 | 说明 |
|---|---|
| 监控(Monitoring) | 定期向所有主从节点及其他哨兵发送 PING 心跳,探测存活 |
| 自动故障转移(Failover) | 主库确认宕机后,自动从健康从库中选出一个升任新主,并重配其他从库挂载 |
| 配置中心与通知(Notification) | 发布订阅机制,实时通知客户端最新的主库 IP 和端口 |
1. 下线判定机制
- 主观下线(sdown):单个哨兵在
down-after-milliseconds内未收到主库有效 PING 回复,主观认为主库失联(防单点网络抖动误判)。 - 客观下线(odown):该哨兵向其他哨兵发送
SENTINEL is-master-down-by-addr询问。当确认主库失联的哨兵数量达到法定阈值(quorum,通常配置为 2/3 以上)时,主库被判定为客观下线。
2. 哨兵 Leader 选举与新主选择
- Raft 选出执行哨兵(Leader):达到客观下线后,各哨兵基于 Raft 协议互相投票,首个获得过半数(> N/2)选票的哨兵成为 Leader,全权负责执行故障转移。
- 从库选主四步过滤法:
- ① 排除已下线、断连以及最近心跳超时的从库;
- ② 优先选择
replica-priority权重最高的从库; - ③ 权重相同时,选择**复制偏移量最大(数据最全、offset 最大)**的从库;
- ④ 仍相同时,选择运行 ID(RunID)字典序最小的从库。
三、脑裂(Split-Brain)与数据丢失防御
脑裂是分布式主从系统在网络分区时最凶险的故障之一:
1. 脑裂是如何产生的?
[网络分区前]
Client ---> Master (192.168.1.10) ---> Replica-1, Replica-2
^
Sentinel 集群
[网络分区后: 假死与双主并存]
【分区 A(少数派)】:
Client-1 ---> [旧 Master (假死/网络隔离)] (客户端依然在向旧主写入数据!)
【分区 B(多数派)】:
Sentinel 集群 (达到 Quorum) ---> 判定旧主客观下线 ---> 选举 Replica-1 升为【新 Master】
Client-2 ---> [新 Master]- 网络分区孤立:旧 Master 与大部分 Sentinel 及从节点失联,但与部分业务 Client 之间的网络仍通畅。
- 哨兵发起切换:多数派 Sentinel 发现旧 Master 心跳超时,将其判定为“客观下线”,投票从 Replica 中选举出新 Master。
- 双主并存:此时系统中同时存在两个“Master”。部分 Client 继续向旧 Master 写入,另一部分 Client 切换到新 Master 写入。
- 毁灭性数据丢失:当网络分区恢复后,Sentinel 会强制将旧 Master 降级为新 Master 的从库(Replica)。从库在首次与新主全量同步前,会执行
FLUSHDB清空自身所有数据并加载新主的 RDB!旧 Master 在脑裂期间接收到的所有客户端写数据彻底永久丢失。
2. 工业界防御脑裂的两大参数
在 Redis 配置文件中开启以下协同参数:
# 主节点必须连接的最小良好从节点数(通常设为从节点总数的过半)
min-replicas-to-write 1
# 从节点向主节点发送 ACK(REPLCONF ACK)的最大允许延迟秒数(如 10 秒)
min-replicas-max-lag 10- 生效机制:
- 主节点每个周期都会检查连接的从库状态与 ACK 延迟。
- 一旦主节点发现健康从库数量
< min-replicas-to-write(例如网络隔离后健康从库为 0),或者从库心跳延迟> 10s,主节点会立即自我拒绝所有写入请求,向客户端返回错误:-NOREPLICAS Not enough good replicas to write. - 这样最多只会丢失发生隔离后 10 秒以内的数据,杜绝旧主无限制接收脏写。
- 代价权衡(CAP 权衡):
- 开启该配置后,如果从库全部挂掉,主库将无法接收写操作(牺牲可用性 A,保住一致性 C)。
- Redis 官方设计哲学始终偏向 AP(高可用与高性能),因此官方默认并未严格强制开启
min-replicas-to-write。面试时需要强调:“Redis 哨兵只能保证秒级恢复可用性(RTO),无法保证绝对零数据丢失(RPO=0)”。
面试追问
- 全量同步和增量同步? 首次或 backlog 溢出全量(RDB + 缓冲增量),正常增量(repl_backlog 补差)。
- 主从复制是同步的吗? 纯异步。主库写成功即返回,从库异步复制。强一致读需直读主库。
- 主观下线和客观下线的区别? 主观下线是单个哨兵的心跳超时判断;客观下线是达到 quorum 多数哨兵达成共识判定。
- 哨兵怎么选新主? 哨兵集群先 Raft 选出执行 Leader 哨兵,再按从库优先级、offset 复制进度、RunID 字典序四步淘汰选主。
- 脑裂怎么防? 配置
min-replicas-to-write和min-replicas-max-lag。当从节点失联或延迟超标时主库主动拒绝写,牺牲部分可用性来控制数据丢失窗口。