Redis学习笔记:哨兵集群实战,自动故障转移与高可用架构完整指南
什么是哨兵集群?
Redis 哨兵(Sentinel)是 Redis 高可用架构的智能大脑。它不存储数据,只做一件事:监控主从集群,并在主库宕机时自动投票选新主库。
解决的问题
主从复制虽然实现了读写分离和数据备份,但有一个致命缺陷:主库挂了没有自动切换机制。需要人工登录服务器执行 SLAVEOF NO ONE 操作,这个过程中服务会中断。
哨兵模式解决了三个核心问题:
- 监控(Monitoring)—— 不断检查主库和从库是否正常运行
- 通知(Notification)—— 当被监控的 Redis 实例出现问题时,哨兵可以通知管理员
- 自动故障转移(Automatic Failover)—— 主库宕机时自动选择一个从库升级为新主库,并通知其他从库复制新主库
与普通主从集群的对比
| 特性 | 普通主从复制 | 哨兵模式 |
|---|---|---|
| 读写分离 | ✅ 支持 | ✅ 支持 |
| 数据热备份 | ✅ 从库全量复制 | ✅ 从库全量复制 |
| 自动故障转移 | ❌ 需要手动干预 | ✅ 自动切换 |
| 主库宕机恢复时间 | 数分钟~数小时(人工) | 10~30秒(自动) |
| 部署复杂度 | ⭐ 低 | ⭐⭐ 中 |
| 额外资源消耗 | 无 | 3 个哨兵节点(轻量) |
核心概念(必读)
在动手之前,先理解 3 个关键机制,否则看日志会一头雾水。
主观下线(SDOWN)
单个哨兵自己发现 ping 不通主库了,它单方面觉得主库挂了。这叫主观下线,只是个人判断,不一定准确(可能只是网络抖动)。
客观下线(ODOWN)
超过半数(quorum)的哨兵都认为主库挂了,形成共识。此时哨兵群确认主库确实宕机,准备开始故障转移。
Quorum(法定人数)
本文配置 3 个哨兵且 quorum=2。这意味着只要有 2 个哨兵认为主库挂了,就触发自动切换。既保证了可靠性,又防止了 1 个哨兵因网络抖动导致误切换。
Tilt 模式(躺平保护)
这是哨兵最容易被忽略但极其重要的保护机制。 当哨兵检测到系统时钟出现异常跳跃,或事件循环耗时超过 2 秒时,会主动进入 Tilt(躺平)模式:
- Tilt 模式下暂停所有故障检测和故障转移操作
- 持续约 30 秒后自动退出
- 目的是防止因系统时间不准或网络卡顿导致脑裂(错误地切换主库)
环境准备
前置知识
本文基于已搭建好的一主两从集群(见 Redis主从集群搭建实战)。如果你还没有主从集群,请先阅读那篇文章。
当前集群拓扑
| 角色 | 服务名 | 容器名 | 端口 |
|---|---|---|---|
| 🟢 主库 | master |
redis-master |
6379 |
| 🟢 从库 1 | replica1 |
redis-replica-1 |
6380 |
| 🟢 从库 2 | replica2 |
redis-replica-2 |
6381 |
哨兵配置文件
在 config/ 目录下创建三个几乎相同的配置文件。唯一的变化是端口不同(但实际上在各自容器中都是 26379,不影响)。
config/sentinel1.conf
port 26379
sentinel monitor mymaster master 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
sentinel resolve-hostnames yes
config/sentinel2.conf / sentinel3.conf
内容完全一样,因为端口在容器内部各自隔离。
参数解读
| 参数 | 值 | 含义 |
|---|---|---|
mymaster |
自定义名称 | 你给这个主从集群起的名字,哨兵通过这个名字来识别 |
master 6379 |
主库地址 | Docker 网络中的服务名和端口,哨兵通过它找到主库 |
2 |
quorum | 法定人数。3 个哨兵中至少 2 个同意,才触发切换 |
down-after-milliseconds |
5000ms | 哨兵 ping 主库超过 5 秒没响应就认为挂了 |
failover-timeout |
10000ms | 故障转移超时限制 |
parallel-syncs |
1 | 切换后同时通知几个从库同步新主库 |
resolve-hostnames |
yes | 延迟解析主机名,避免启动时因 DNS 未就绪而崩溃 |
更新 docker-compose.yml
在原有主从配置的基础上,追加 3 个哨兵服务。注意与 services 同级缩进:
# ---------- 哨兵节点 1 ----------
sentinel1:
image: redis:7.2.4
container_name: redis-sentinel-1
restart: always
ports:
- "26379:26379"
volumes:
- ./config/sentinel1.conf:/usr/local/etc/redis/sentinel.conf
- ./data/sentinel1:/data
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
depends_on:
- master
- replica1
- replica2
networks:
- redis-net
# ---------- 哨兵节点 2 ----------
sentinel2:
image: redis:7.2.4
container_name: redis-sentinel-2
restart: always
ports:
- "26380:26379"
volumes:
- ./config/sentinel2.conf:/usr/local/etc/redis/sentinel.conf
- ./data/sentinel2:/data
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
depends_on:
- master
- replica1
- replica2
networks:
- redis-net
# ---------- 哨兵节点 3 ----------
sentinel3:
image: redis:7.2.4
container_name: redis-sentinel-3
restart: always
ports:
- "26381:26379"
volumes:
- ./config/sentinel3.conf:/usr/local/etc/redis/sentinel.conf
- ./data/sentinel3:/data
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
depends_on:
- master
- replica1
- replica2
networks:
- redis-net
启动哨兵并验证监控状态
启动集群
docker-compose up -d
验证哨兵是否成功监控主库
进入任一哨兵容器查看主库状态:
docker exec -it redis-sentinel-1 redis-cli -p 26379
> SENTINEL master mymaster
关键输出解读:
| 字段 | 健康值 | 含义 |
|---|---|---|
flags |
master |
主库在线 |
num-slaves |
2 |
检测到 2 个从库 |
num-other-sentinels |
2 |
发现另外 2 个哨兵伙伴 |
quorum |
2 |
法定人数配置生效 |
查看从库列表
> SENTINEL replicas mymaster
会列出两个从库的 IP、端口、复制偏移量和连接状态。
测试故障转移(核心实验)
问题:使用容器名 DNS 无法触发故障转移
如果哨兵配置中使用的是主机名 master,执行 docker stop redis-master 后,哨兵日志会出现:
Failed to resolve hostname 'master'
+sdown master mymaster 172.23.0.2 6379
+tilt #tilt mode entered
原因:Docker 的内部 DNS 解析 master 主机名时有 1-2 秒的超时重试,导致哨兵的事件循环被阻塞超过 2 秒,触发 Tilt 躺平保护模式。在 Tilt 模式下哨兵禁止执行故障转移,导致切换永远无法完成,形成 +tilt → -tilt → 又 +tilt 的死循环。
解决方案:把主机名改成固定 IP
第一步:查询主库当前 IP
docker inspect redis-master | grep IPAddress
输出示例:"IPAddress": "172.23.0.2"
第二步:修改 3 个哨兵配置文件
将 sentinel monitor mymaster master 6379 2 改为:
sentinel monitor mymaster 172.23.0.2 6379 2
只要不执行
docker-compose down(不会删除网络),Docker 会一直将这个 IP 分配给该容器。
第三步:重启哨兵
docker-compose restart sentinel1 sentinel2 sentinel3
第四步:验证哨兵已监控正确的 IP
docker exec -it redis-sentinel-1 redis-cli -p 26379 SENTINEL master mymaster
确认 ip 字段为 172.23.0.2。
执行故障转移
第一步:开一个终端实时查看哨兵日志
docker logs redis-sentinel-1 --tail 30 -f
第二步:另一个终端执行停库
docker stop redis-master
第三步:观察日志
因为哨兵现在直接通过 IP 探测主库,docker stop 会让 TCP 连接瞬间收到 RST 包,毫秒级返回失败,完全不会触发 Tilt。你会看到清晰的切换流程:
+sdown master mymaster 172.23.0.2 6379
+odown master mymaster 172.23.0.2 6379 #quorum 2/2
+new-epoch 1
+try-failover master mymaster
+vote-for-leader ...
+elected-leader master mymaster
+failover-state-select-slave master
+switch-master mymaster 172.23.0.2 6379 172.23.0.4 6380 ← 切换成功!
第四步:查询新主库
docker exec -it redis-sentinel-1 redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
返回新的主库 IP 和端口(如 172.23.0.4:6380)。
验证新主库写入
# 进入新主库写入数据
docker exec -it redis-replica-2 redis-cli -p 6381
127.0.0.1:6381> set status "failover-success"
OK
127.0.0.1:6381> get status
"failover-success"
恢复旧主库,观察自动降级
docker start redis-master
# 等待 5 秒后查看它的角色
docker exec -it redis-master redis-cli -p 6379 INFO replication
你会惊讶地发现,旧主库的 role 已经变成了 slave,自动认了新主库做大哥。这就是哨兵在后台自动 CONFIG REWRITE 的神奇之处。
完整的切换流程原理
你亲手验证了 Redis 自动故障转移的完整闭环:
- 监控:哨兵们通过 PING 检测到主库失联(
+sdown) - 投票:由于配置了
quorum=2,其他哨兵也报告连不上,触发+odown - 选举:哨兵们内部选出一个领导者(
+elected-leader) - 挑选:领导者从从库中挑选数据最新的(
slave-repl-offset最大)执行升主 - 切换:执行
REPLICAOF NO ONE提升为新的主库(+switch-master) - 重配置:通知其他从库去复制新主库,并强制旧主库重新上线后成为从库
大坑总结与反思
Docker DNS 超时导致 Tilt 死循环
这是 Docker + Sentinel 最常见的坑。主机名 master 的 DNS 解析有 1-2 秒超时延迟,导致哨兵事件循环阻塞,触发 Tilt 保护。修复方案是使用固定 IP 或添加 sentinel resolve-hostnames yes。
知识图谱
| 层级 | 组件 | 掌握程度 |
|---|---|---|
| 📦 数据存储 | 1 主 2 从 | ✅ 熟练(偏移量、全量/增量同步) |
| 🧠 自动切换 | 3 哨兵 | ✅ 刚跑通(SDOWN/ODOWN/投票选举/Tilt) |
| 🏔️ 待攻克 | Redis Cluster(分片) | ❓ 准备就绪 |
总结
本文从一主两从集群出发,逐步搭建了 3 节点哨兵高可用架构,并亲手验证了自动故障转移流程。核心收获:
- 哨兵配置:
sentinel monitor、quorum、超时参数的含义 - 故障转移 6 步流程:SDOWN → ODOWN → 选举 → 选从 → 切换 → 重配置
- Tilt 保护机制:Docker DNS 超时导致的死循环及修复方案
- IP 与主机名的选择:生产环境中建议使用 IP 以避免 DNS 抖动
以上是 Redis 哨兵模式的完整动手实践。下一阶段可以学习 Redis Cluster(分片集群)——它将哨兵的故障转移能力内嵌到每个节点中,并使用哈希槽实现数据水平扩展,且完全抛弃 DNS 解析,从根本上避免本文遇到的 Tilt 问题。

浙公网安备 33010602011771号