3. Redis Sentinel 高可用:架构、选举、故障转移与客户端感知
在读写分离架构中,Redis Sentinel 是保障主节点故障时自动切换、维持写入入口高可用的核心组件。本章深入 Sentinel 的分布式哨兵架构、Leader 选举算法、故障转移流程以及客户端如何感知拓扑变化。
3.1 Sentinel 架构与核心职责
Sentinel 本身是一个独立运行的 Redis 特殊进程,通常部署 3 或 5 个实例组成哨兵集群。它不存储业务数据,只监控 Redis 数据节点的状态并执行管理动作。
| 职责 | 说明 |
|---|---|
| 监控 | 定期向所有主、从节点发送 PING,检测可达性与响应时间 |
| 通知 | 当被监控节点状态变化时,通过 Pub/Sub 或 API 通知管理员或客户端 |
| 自动故障转移 | 主节点不可达时,从剩余从节点中选举一个提升为新主,并修改其他从节点的复制目标 |
| 配置提供 | 客户端通过 Sentinel 获取当前主节点地址,实现动态发现 |
部署建议:Sentinel 至少部署 3 个实例(奇数),且分布在不同的物理机或可用区,避免脑裂时无法形成法定人数。
3.2 哨兵选举:Raft 风格的 Leader 选举
当 Sentinel 集群需要执行故障转移时,必须先选出一个 Leader 哨兵 来协调整个流程。选举基于 Raft 算法 的简化实现,核心步骤:
- 主观下线(SDOWN):单个 Sentinel 发现主节点 PING 无响应(
down-after-milliseconds超时)。 - 客观下线(ODOWN):该 Sentinel 向其他哨兵发送
SENTINEL is-master-down-by-addr命令,当收到 法定人数(quorum) 的确认后,标记主节点为 ODOWN。 - 发起选举:检测到 ODOWN 的 Sentinel 自增本地
epoch(选举轮次),并向所有哨兵广播投票请求。 - 投票规则:每个 Sentinel 在每个 epoch 内只能投一票,先到先得;投票给请求中
runid最大的哨兵(或最先收到的请求)。 - 成为 Leader:获得超过半数(
N/2 + 1)投票的 Sentinel 成为 Leader,开始执行故障转移。
关键参数:
quorum 通常设为 N/2 + 1(例如 3 个哨兵设 2,5 个哨兵设 3)。down-after-milliseconds 建议 5000~10000ms,避免网络抖动导致频繁切换。
3.3 故障转移机制:Leader 执行四步切换
Leader 哨兵被选出后,立即执行故障转移。整个过程分为四个严格顺序的阶段:
| 步骤 | 操作 | 详细说明 |
|---|---|---|
| 1 | 筛选从节点 | 排除已下线、断线时间超过 down-after-milliseconds * 10、与主节点复制延迟过大的从节点 |
| 2 | 选举新主节点 | 在剩余从节点中,按优先级(slave-priority)从低到高选择;优先级相同则选复制偏移量最大的(数据最新) |
| 3 | 提升新主节点 | 向选中的从节点发送 SLAVEOF NO ONE,使其成为新主节点;等待其状态变为 master |
| 4 | 重新配置从节点 | 向其他从节点发送 SLAVEOF new_master_ip new_master_port,使其复制新主节点;若原主节点恢复,也将其降级为从节点 |
注意:故障转移期间,原主节点写入的数据会丢失(异步复制)。读写分离架构中,从节点可能读到旧数据,需业务层容忍或使用
WAIT 命令同步复制。
3.4 客户端感知:动态发现与连接管理
故障转移后,主节点 IP 发生变化。客户端不能硬编码主节点地址,必须通过 Sentinel 动态获取当前主节点信息。
3.4.1 客户端连接模式
- 直接连接 Sentinel:客户端先连接 Sentinel 集群,通过
SENTINEL get-master-addr-by-name master_name获取当前主节点地址。 - 订阅变更通知:客户端订阅 Sentinel 的
+switch-master频道,当主节点切换时收到推送,主动更新连接。 - 连接池刷新:客户端维护一个主节点连接池,定期或通过回调刷新地址,避免使用失效连接。
3.4.2 常见客户端实现
| 客户端 | Sentinel 支持方式 | 自动故障转移感知 |
|---|---|---|
| Jedis (Java) | JedisSentinelPool |
内置 MasterListener 订阅 +switch-master,自动重建连接池 |
| Lettuce (Java) | RedisSentinelClient |
基于响应式,支持拓扑刷新和重连 |
| redis-py (Python) | RedisSentinel 类 |
每次操作前检查主节点地址,或订阅变更 |
| go-redis (Go) | FailoverOptions |
内置 Sentinel 监控,自动切换连接 |
最佳实践:客户端应设置连接超时(
connectTimeout)和重试策略,避免在故障转移期间长时间阻塞。建议重试次数 3~5 次,间隔 100~500ms。
3.5 读写分离架构中的 Sentinel 配置示例
以下是一个典型的 Sentinel 配置文件(sentinel.conf)关键项:
# 监控的主节点名称、IP、端口、法定人数
sentinel monitor mymaster 192.168.1.10 6379 2
# 主观下线判定时间(毫秒)
sentinel down-after-milliseconds mymaster 5000
# 故障转移超时(毫秒)
sentinel failover-timeout mymaster 30000
# 并行同步的从节点数量(故障转移时)
sentinel parallel-syncs mymaster 1
# 保护模式(生产环境建议关闭或配置密码)
protected-mode no
# 哨兵之间通信的密码(可选)
sentinel auth-pass mymaster your_password
参数调优:
parallel-syncs 设为 1 可减少故障转移期间从节点的同步压力;failover-timeout 应大于 down-after-milliseconds * 2 + 选举时间。
3.6 脑裂与数据丢失场景
Sentinel 无法完全避免脑裂(网络分区导致主节点和哨兵集群失联)。典型场景:
- 主节点与哨兵集群网络中断:哨兵判定主节点下线,执行故障转移,提升新主节点。
- 原主节点恢复网络:原主节点仍以
master角色运行,但哨兵已将其降级为从节点。 - 数据丢失:原主节点在分区期间写入的数据未同步到新主节点,恢复后这些数据被覆盖或丢失。
缓解措施:使用
min-slaves-to-write 和 min-slaves-max-lag 参数,限制主节点在从节点数量不足或延迟过大时拒绝写入,减少脑裂期间的数据丢失。
通过本章的学习,你应该能够独立部署 Sentinel 集群、理解选举与故障转移的底层机制,并指导客户端正确感知拓扑变化,为读写分离架构提供高可用保障。