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 yesconfig/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.4container_name:redis-sentinel-1restart:alwaysports:-"26379:26379"volumes:-./config/sentinel1.conf:/usr/local/etc/redis/sentinel.conf-./data/sentinel1:/datacommand:redis-sentinel /usr/local/etc/redis/sentinel.confdepends_on:-master-replica1-replica2networks:-redis-net# ---------- 哨兵节点 2 ----------sentinel2:image:redis:7.2.4container_name:redis-sentinel-2restart:alwaysports:-"26380:26379"volumes:-./config/sentinel2.conf:/usr/local/etc/redis/sentinel.conf-./data/sentinel2:/datacommand:redis-sentinel /usr/local/etc/redis/sentinel.confdepends_on:-master-replica1-replica2networks:-redis-net# ---------- 哨兵节点 3 ----------sentinel3:image:redis:7.2.4container_name:redis-sentinel-3restart:alwaysports:-"26381:26379"volumes:-./config/sentinel3.conf:/usr/local/etc/redis/sentinel.conf-./data/sentinel3:/datacommand:redis-sentinel /usr/local/etc/redis/sentinel.confdepends_on:-master-replica1-replica2networks:-redis-net启动哨兵并验证监控状态
启动集群
docker-composeup-d验证哨兵是否成功监控主库
进入任一哨兵容器查看主库状态:
dockerexec-itredis-sentinel-1 redis-cli-p26379>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
dockerinspect redis-master|grepIPAddress输出示例:"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-composerestart sentinel1 sentinel2 sentinel3第四步:验证哨兵已监控正确的 IP
dockerexec-itredis-sentinel-1 redis-cli-p26379SENTINEL master mymaster确认ip字段为172.23.0.2。
执行故障转移
第一步:开一个终端实时查看哨兵日志
dockerlogs redis-sentinel-1--tail30-f第二步:另一个终端执行停库
dockerstop 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 ← 切换成功!第四步:查询新主库
dockerexec-itredis-sentinel-1 redis-cli-p26379SENTINEL get-master-addr-by-name mymaster返回新的主库 IP 和端口(如172.23.0.4:6380)。
验证新主库写入
# 进入新主库写入数据dockerexec-itredis-replica-2 redis-cli-p6381127.0.0.1:6381>setstatus"failover-success"OK127.0.0.1:6381>get status"failover-success"恢复旧主库,观察自动降级
dockerstart redis-master# 等待 5 秒后查看它的角色dockerexec-itredis-master redis-cli-p6379INFO 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 问题。
