写在前面
主从复制解决了数据冗余和读写分离的问题,但主节点故障时需要人工干预切换,这在生产环境中是不可接受的。Redis哨兵(Sentinel)机制实现了主节点的自动故障转移,是Redis高可用的核心组件。今天我们深入理解哨兵机制的原理与实践。

文章目录
-
- 写在前面
- 一、为什么需要哨兵?
-
- [1.1 主从架构的痛点](#1.1 主从架构的痛点)
- [1.2 哨兵的作用](#1.2 哨兵的作用)
- 二、哨兵的核心功能
-
- [2.1 监控(Monitoring)](#2.1 监控(Monitoring))
- [2.2 通知(Notification)](#2.2 通知(Notification))
- [2.3 自动故障转移(Automatic Failover)](#2.3 自动故障转移(Automatic Failover))
- [2.4 配置提供者(Configuration Provider)](#2.4 配置提供者(Configuration Provider))
- 三、哨兵配置详解
-
- [3.1 哨兵配置文件](#3.1 哨兵配置文件)
- [3.2 配置参数说明](#3.2 配置参数说明)
- [3.3 quorum配置详解](#3.3 quorum配置详解)
- [3.4 启动哨兵](#3.4 启动哨兵)
- 四、哨兵选举原理
-
- [4.1 领导者哨兵选举](#4.1 领导者哨兵选举)
- [4.2 新主节点选举](#4.2 新主节点选举)
- 五、故障转移详细流程
-
- [5.1 完整故障转移流程](#5.1 完整故障转移流程)
- [5.2 客户端感知](#5.2 客户端感知)
- 六、踩坑提醒
- 七、哨兵监控与运维
-
- [7.1 常用命令](#7.1 常用命令)
- [7.2 监控指标](#7.2 监控指标)
- [7.3 运维建议](#7.3 运维建议)
- 八、面试高频考点
- 九、参考资料
- 十、互动话题
一、为什么需要哨兵?
1.1 主从架构的痛点
实际场景:某公司Redis主节点在凌晨3点宕机,运维人员收到告警后手动切换主从,整个过程耗时30分钟,期间服务不可用,造成严重的业务损失。
主从架构存在的问题:
| 问题 | 说明 |
|---|---|
| 单点故障 | 主节点宕机后无法写入 |
| 人工干预 | 故障切换需要人工操作 |
| 切换时间长 | 人工响应慢,服务中断时间长 |
| 配置变更 | 切换后需要修改应用配置 |
1.2 哨兵的作用
Redis Sentinel提供以下功能:
| 功能 | 说明 |
|---|---|
| 监控 | 持续检查主从节点是否正常运行 |
| 通知 | 节点故障时通知管理员或其他应用 |
| 自动故障转移 | 主节点故障时自动选举新主节点 |
| 配置提供者 | 为客户端提供当前主节点地址 |
| 仲裁者 | 多个哨兵共同决策,避免误判 |
┌─────────────────────────────────────────────────────────┐
│ Sentinel Cluster │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Sentinel-1│ │Sentinel-2│ │Sentinel-3│ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ │ 监控 & 投票 │
└─────────────────────┼───────────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Master │ │ Slave1 │ │ Slave2 │
└──────────┘ └──────────┘ └──────────┘
二、哨兵的核心功能
2.1 监控(Monitoring)
哨兵定期向主从节点发送心跳检测:
| 检测类型 | 频率 | 说明 |
|---|---|---|
| PING | 每秒 | 检测节点是否存活 |
| INFO | 每10秒 | 获取主从拓扑信息 |
| PUBLISH | 每2秒 | 发布哨兵信息到__sentinel__:hello频道 |
主观下线(S_DOWN):
- 单个哨兵认为节点下线
- 配置:
down-after-milliseconds
客观下线(O_DOWN):
- 多数哨兵认为主节点下线
- 触发故障转移流程
2.2 通知(Notification)
哨兵可以通过以下方式通知故障:
redis
# 订阅哨兵频道
SUBSCRIBE __sentinel__:hello
# 订阅故障转移事件
SUBSCRIBE +switch-master
SUBSCRIBE +sdown
SUBSCRIBE +odown
2.3 自动故障转移(Automatic Failover)
故障转移流程:
┌──────────────────────────────────────────────────────────┐
│ 故障转移流程 │
├──────────────────────────────────────────────────────────┤
│ 1. 哨兵检测到主节点客观下线 │
│ ↓ │
│ 2. 哨兵之间投票选举领导者哨兵 │
│ ↓ │
│ 3. 领导者哨兵从从节点中选举新主节点 │
│ ↓ │
│ 4. 其他从节点复制新主节点 │
│ ↓ │
│ 5. 更新主节点配置,通知客户端 │
│ ↓ │
│ 6. 旧主节点恢复后变为从节点 │
└──────────────────────────────────────────────────────────┘
2.4 配置提供者(Configuration Provider)
客户端连接哨兵获取主节点地址:
redis
# 客户端请求主节点地址
SENTINEL get-master-addr-by-name mymaster
# 返回
1) "192.168.1.101"
2) "6379"
三、哨兵配置详解
3.1 哨兵配置文件
conf
# sentinel.conf
# 端口
port 26379
# 监控主节点
# sentinel monitor <master-name> <ip> <port> <quorum>
sentinel monitor mymaster 192.168.1.100 6379 2
# 主节点密码
sentinel auth-pass mymaster your_password
# 主观下线时间
sentinel down-after-milliseconds mymaster 30000
# 故障转移超时时间
sentinel failover-timeout mymaster 180000
# 同时可以对新主节点进行复制的从节点数量
sentinel parallel-syncs mymaster 1
# 工作目录
dir "/var/redis/sentinel"
# 日志级别
loglevel notice
# 日志文件
logfile "/var/log/redis/sentinel.log"
# 后台运行
daemonize yes
# PID文件
pidfile "/var/run/redis-sentinel.pid"
3.2 配置参数说明
| 参数 | 说明 | 默认值 |
|---|---|---|
quorum |
判断客观下线需要的哨兵数量 | - |
down-after-milliseconds |
主观下线时间 | 30000ms |
failover-timeout |
故障转移超时时间 | 180000ms |
parallel-syncs |
同时复制的从节点数 | 1 |
3.3 quorum配置详解
经验之谈:quorum的配置需要根据哨兵数量合理设置,一般设置为哨兵数量的半数以上。
quorum的作用:
- 判断主节点是否客观下线
- 选举领导者哨兵时需要多数哨兵同意
推荐配置:
| 哨兵数量 | quorum | 说明 |
|---|---|---|
| 1 | 1 | 不推荐,单点故障 |
| 3 | 2 | 推荐,允许1个哨兵故障 |
| 5 | 3 | 推荐,允许2个哨兵故障 |
3.4 启动哨兵
shell
# 方式一:使用配置文件启动
redis-sentinel /etc/redis/sentinel.conf
# 方式二:使用redis-server启动
redis-server /etc/redis/sentinel.conf --sentinel
四、哨兵选举原理
4.1 领导者哨兵选举
当主节点被判定为客观下线后,需要选举一个领导者哨兵来执行故障转移。
选举流程:
┌─────────────────────────────────────────────────────────┐
│ 领导者哨兵选举流程 │
├─────────────────────────────────────────────────────────┤
│ 1. 发现主节点客观下线的哨兵成为候选者 │
│ ↓ │
│ 2. 候选者向其他哨兵发送SENTINEL is-master-down-by-addr │
│ ↓ │
│ 3. 其他哨兵收到请求后,每个epoch只投票一次 │
│ ↓ │
│ 4. 获得多数票的候选者成为领导者哨兵 │
│ ↓ │
│ 5. 领导者哨兵执行故障转移 │
└─────────────────────────────────────────────────────────┘
选举规则:
- 使用Raft算法进行选举
- 每个配置纪元(epoch)只能投票一次
- 获得多数票的哨兵成为领导者
4.2 新主节点选举
领导者哨兵从从节点中选举新主节点,选举规则:
| 优先级 | 规则 | 说明 |
|---|---|---|
| 1 | 排除下线节点 | 不选择已下线的从节点 |
| 2 | 排除断开时间长的节点 | down-after-milliseconds * 10 |
| 3 | 优先级配置 | replica-priority值小的优先 |
| 4 | 复制偏移量 | slave_repl_offset大的优先 |
| 5 | runid | runid小的优先(兜底) |
从节点配置优先级:
conf
# 从节点配置
replica-priority 100 # 数值越小优先级越高,0表示不参与选举
五、故障转移详细流程
5.1 完整故障转移流程
时间线
│
│ ┌─────────────────────────────────────────────┐
│ │ 1. 哨兵检测到主节点主观下线 │
│ └─────────────────────────────────────────────┘
│
│ ┌─────────────────────────────────────────────┐
│ │ 2. 多数哨兵确认,主节点客观下线 │
│ └─────────────────────────────────────────────┘
│
│ ┌─────────────────────────────────────────────┐
│ │ 3. 哨兵之间投票选举领导者哨兵 │
│ └─────────────────────────────────────────────┘
│
│ ┌─────────────────────────────────────────────┐
│ │ 4. 领导者哨兵选择新主节点 │
│ └─────────────────────────────────────────────┘
│
│ ┌─────────────────────────────────────────────┐
│ │ 5. 新主节点执行 REPLICAOF NO ONE │
│ └─────────────────────────────────────────────┘
│
│ ┌─────────────────────────────────────────────┐
│ │ 6. 其他从节点复制新主节点 │
│ └─────────────────────────────────────────────┘
│
│ ┌─────────────────────────────────────────────┐
│ │ 7. 更新哨兵配置,发布切换事件 │
│ └─────────────────────────────────────────────┘
│
│ ┌─────────────────────────────────────────────┐
│ │ 8. 旧主节点恢复后变为从节点 │
│ └─────────────────────────────────────────────┘
▼
5.2 客户端感知
客户端通过以下方式感知主节点变化:
方式一:订阅事件
redis
# 订阅主节点切换事件
SUBSCRIBE +switch-master
# 收到消息格式
# +switch-master
# mymaster
# 192.168.1.100 6379 # 旧主节点
# 192.168.1.101 6379 # 新主节点
方式二:主动查询
redis
# 获取当前主节点地址
SENTINEL get-master-addr-by-name mymaster
方式三:连接池自动发现
java
// Jedis示例
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.1.100:26379");
sentinels.add("192.168.1.101:26379");
sentinels.add("192.168.1.102:26379");
JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels);
Jedis jedis = pool.getResource(); // 自动获取当前主节点
六、踩坑提醒
踩坑提醒:脑裂问题
问题描述:
网络分区导致主节点与哨兵隔离,哨兵选举出新主节点,但旧主节点仍在接收写入,导致数据不一致。
┌─────────────────────────────────────────────────────────┐
│ 网络分区 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │ ┌─────────────┐ │
│ │ 旧 Master │ │ │ 新 Master │ │
│ │ (隔离区) │ │ │ (多数区) │ │
│ └─────────────┘ │ └─────────────┘ │
│ │ │ │ │
│ ▼ │ ▼ │
│ ┌─────────────┐ │ ┌─────────────┐ │
│ │ Client │ │ │ Client │ │
│ │ (写入旧主) │ │ │ (写入新主) │ │
│ └─────────────┘ │ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
解决方案:
conf
# 主节点配置
# 至少有1个从节点才能写入
min-replicas-to-write 1
# 从节点延迟不超过10秒
min-replicas-max-lag 10
配置说明:
- 如果从节点数量不足或延迟过高,主节点拒绝写入
- 防止网络分区时旧主节点继续接收写入
- 恢复后旧主节点的数据会被新主节点覆盖
踩坑提醒:哨兵数量不足
问题描述:
哨兵数量为2个时,一个哨兵故障后无法进行故障转移(无法达到多数票)。
解决方案:
- 哨兵数量至少3个,建议奇数个(3、5、7)
- 哨兵部署在不同服务器上
踩坑提醒:配置文件覆盖
问题描述:
哨兵运行时会自动修改配置文件,记录主从拓扑变化。如果手动修改配置文件可能被覆盖。
解决方案:
- 哨兵配置文件需要可写权限
- 修改配置后重启哨兵
- 使用
SENTINEL RESET命令重置配置
七、哨兵监控与运维
7.1 常用命令
redis
# 查看主节点信息
SENTINEL master mymaster
# 查看从节点列表
SENTINEL replicas mymaster
# 查看哨兵列表
SENTINEL sentinels mymaster
# 获取主节点地址
SENTINEL get-master-addr-by-name mymaster
# 检查主节点是否下线
SENTINEL is-master-down-by-addr 192.168.1.100 6379
# 强制故障转移
SENTINEL failover mymaster
# 重置哨兵配置
SENTINEL RESET mymaster
7.2 监控指标
| 指标 | 说明 | 告警阈值 |
|---|---|---|
master_link_status |
主从连接状态 | down |
slave_repl_offset |
从节点复制偏移量 | 与主节点差距大 |
s_down |
主观下线 | 触发告警 |
o_down |
客观下线 | 触发告警 |
num-other-sentinels |
其他哨兵数量 | 少于预期 |
7.3 运维建议
| 建议 | 说明 |
|---|---|
| 哨兵数量 | 至少3个,奇数个 |
| 部署位置 | 不同服务器,跨机房部署 |
| 监控告警 | 监控主从状态、复制延迟 |
| 配置备份 | 定期备份哨兵配置文件 |
| 测试演练 | 定期进行故障转移演练 |
八、面试高频考点
考点1:哨兵如何判断主节点下线?
答案:
两步判断:
-
主观下线(S_DOWN)
- 单个哨兵在
down-after-milliseconds时间内未收到主节点PONG响应 - 标记为主观下线
- 单个哨兵在
-
客观下线(O_DOWN)
- 当主观下线的哨兵数量达到
quorum配置值 - 标记为客观下线,触发故障转移
哨兵1: 主观下线 ────┐
哨兵2: 主观下线 ────┼───> 客观下线 (quorum=2)
哨兵3: 正常 ────┘ - 当主观下线的哨兵数量达到
考点2:哨兵如何选举新主节点?
答案:
选举规则(按优先级):
- 排除不健康节点:下线或断开时间长的节点
- 优先级配置 :
replica-priority值小的优先 - 复制偏移量 :
slave_repl_offset大的优先(数据最新) - runid:runid小的优先(兜底规则)
配置示例:
conf
# 从节点配置
replica-priority 100 # 优先级,数值越小越优先
考点3:哨兵的数量如何配置?
答案:
| 哨兵数量 | quorum | 容错能力 | 说明 |
|---|---|---|---|
| 1 | 1 | 0 | 不推荐 |
| 2 | 1 | 0 | 不推荐 |
| 3 | 2 | 1 | 推荐 |
| 5 | 3 | 2 | 推荐 |
原则:
- 哨兵数量 ≥ 3,且为奇数
- quorum = 哨兵数量 / 2 + 1
- 允许故障的哨兵数量 = 哨兵数量 - quorum
考点4:什么是脑裂?如何解决?
答案:
脑裂问题:
网络分区导致旧主节点与多数哨兵隔离,哨兵选举出新主节点,但旧主节点仍在接收写入,造成数据不一致。
解决方案:
conf
# 主节点配置
min-replicas-to-write 1 # 至少有1个从节点才能写入
min-replicas-max-lag 10 # 从节点延迟不超过10秒
原理:
- 网络分区后,旧主节点失去从节点连接
- 配置条件不满足,旧主节点拒绝写入
- 避免数据不一致
九、参考资料
十、互动话题
- 你的生产环境哨兵是如何部署的?遇到过什么问题?
- 如何设计一个跨机房的哨兵架构?需要考虑哪些因素?
- 哨兵故障转移过程中,客户端如何保证写入不丢失?
欢迎在评论区分享你的经验和见解!
恭喜你完成了Redis系列文章的学习!希望这个系列对你的Redis学习和面试有所帮助!