news 2026/10/16 12:25:42

分布式系统——Redis哨兵:自动故障转移与选举原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
分布式系统——Redis哨兵:自动故障转移与选举原理

文章目录

  • 一、概念简述
    • 1.1 手动恢复主从:为什么不靠谱
    • 1.2 哨兵是什么
    • 1.3 哨兵自动恢复流程
    • 1.4 哨兵的核心功能
  • 二、实战搭建
    • 2.1 配置主从节点
    • 2.2 配置哨兵节点
    • 2.3 模拟主节点故障
    • 2.4 旧主节点恢复
  • 三、故障转移的原理
    • 3.1 主观下线与客观下线
    • 3.2 选举 leader 哨兵
    • 3.3 leader 如何挑选新的主节点
    • 3.4 新主确定之后
  • 四、总结与局限

一、概念简述

上一篇讲了主从复制,它提高了读的并发和可用性,但留下了一个致命的问题:主节点挂了怎么办?从节点不会自动晋升为主节点,整个系统就没法写了。哨兵(Sentinel)就是用自动化的手段解决这个问题的。

1.1 手动恢复主从:为什么不靠谱

主节点挂了之后,如果靠人工处理,流程是这样的:

  1. 监控系统报警,通知程序员。
  2. 程序员登录上去看看,能不能抢救。
  3. 抢救不了,就从从节点里挑一个作为新的主节点,执行slaveof no one。
  4. 修改其他从节点,通过slaveof让它们认新的主节点。
  5. 告知客户端,让它们连接新的主节点。
  6. 旧主节点修好之后,作为一个新的从节点挂回集群。

这样的手动修复有两个问题:

  • 繁琐,容易出错:步骤多,一旦出错后果更严重。
  • 慢:即使程序员第一时间响应,也需要一段时间,这段时间系统是不可写的。

1.2 哨兵是什么

  • 哨兵是独立的进程(redis-sentinel),不负责存储数据,只负责监控。
  • 哨兵通常不是单个,而是一个集合(后面会解释原因)。
  • 它监控的是 Redis 的数据节点(主节点和从节点),并在主节点出问题时自动处理。

1.3 哨兵自动恢复流程

  1. 哨兵节点不断给 Redis 数据节点发送心跳包(PING),检测是否正常。
  2. 主节点挂了,一个哨兵发现后,还需要多个哨兵认同,才能确认主节点真的挂了,以防误判。
  3. 哨兵们选举出一个 leader,由它来负责这次故障转移。
  4. leader 从从节点中挑选出新的主节点,自动控制它执行slaveof no one,并控制其他从节点执行slaveof,改认新的主节点。
  5. 客户端通过哨兵得知新的主节点是谁,之后的写操作就会发给新的主节点。
  6. 旧主节点恢复后,哨兵会自动把它变成新主节点的从节点。

1.4 哨兵的核心功能

  • 监控:持续检查主节点、从节点是否正常。

  • 自动故障转移:主节点挂了,自动选出新主,并调整主从关系。

  • 通知:把故障转移的结果通知给客户端(和管理员)。

  • 细节:客户端其实不会直接写死 Redis 的地址,而是先连哨兵,问它当前的主节点是谁(sentinel get-master-addr-by-name),所以哨兵还承担了"配置提供者"的角色。

为什么哨兵一个节点也能完成完整的功能,却还要部署多个?

①哨兵本身也可能挂:如果只有一个哨兵,它挂了,这时候再碰上 Redis 主节点也挂了,就没人做自动恢复了。分布式系统中应当避免出现单点。
②误判的概率高:网络传输容易延迟、丢包,一个哨兵连不上主节点,不代表主节点真挂了,可能只是这个哨兵到主节点的网络有问题。需要多个哨兵投票才能降低误判。

哨兵节点为什么最好是奇数个?
哨兵的决策都是靠多数派(超过半数)达成的。3 个哨兵,多数是 2,允许挂 1 个;4 个哨兵,多数是 3,还是只允许挂 1 个。多出来的那一个并没有提升容错能力,反而多了一份开销。所以选奇数个(3、5)就够了。

二、实战搭建

准备 6 个节点:1 主 2 从 + 3 哨兵,使用Docker Compose来搭建。

注意:这里为了演示,把所有节点放在同一台服务器上。实际应用中,所有节点放在一台服务器上是没有意义的(服务器挂了就全挂了),应该分布在不同的主机上。

2.1 配置主从节点

创建master-slave/docker-compose.yml:

version:'3.7'services:master:image:'redis:5.0.9'container_name:redis-masterrestart:alwayscommand:redis-server--appendonly yesports:-6379:6379slave1:image:'redis:5.0.9'container_name:redis-slave1restart:alwayscommand:redis-server--appendonly yes--slaveof redis-master 6379ports:-6380:6379slave2:image:'redis:5.0.9'container_name:redis-slave2restart:alwayscommand:redis-server--appendonly yes--slaveof redis-master 6379ports:-6381:6379

启动容器:

docker-composeup-d
  • 细节:--slaveof redis-master 6379中直接使用了容器名,因为同一个docker网络内,容器之间可以通过名字互相访问。

2.2 配置哨兵节点

创建redis-sentinel/docker-compose.yml:

version:'3.7'services:sentinel1:image:'redis:5.0.9'container_name:redis-sentinel-1restart:alwayscommand:redis-sentinel /etc/redis/sentinel.confvolumes:-./sentinel1.conf:/etc/redis/sentinel.confports:-26379:26379sentinel2:image:'redis:5.0.9'container_name:redis-sentinel-2restart:alwayscommand:redis-sentinel /etc/redis/sentinel.confvolumes:-./sentinel2.conf:/etc/redis/sentinel.confports:-26380:26379sentinel3:image:'redis:5.0.9'container_name:redis-sentinel-3restart:alwayscommand:redis-sentinel /etc/redis/sentinel.confvolumes:-./sentinel3.conf:/etc/redis/sentinel.confports:-26381:26379networks:default:external:name:master-slave_default
  • 注意:networks这一段是让哨兵容器加入主从节点所在的网络(master-slave_default),这样哨兵才能通过容器名访问到redis-master。

再创建sentinel1.conf、sentinel2.conf、sentinel3.conf三份文件,内容完全相同,都放在/root/redis-sentinel/目录中:

bind0.0.0.0 port26379sentinel monitor redis-master redis-master63792sentinel down-after-milliseconds redis-master1000

逐行解释:

  • bind 0.0.0.0:允许任意地址访问。
  • port 26379:哨兵的端口(Redis 默认是 6379,哨兵默认是 26379)。
  • sentinel monitor <名字> <ip> <port> <quorum>:
    • <名字>:给被监控的主节点起的名字,这里叫redis-master。
    • <ip> <port>:主节点的地址,这里用的是容器名。
    • <quorum>:法定票数。至少有多少个哨兵认为主节点挂了,才会判定为客观下线。这里是 2,通常设为"哨兵总数的一半加一"。
  • sentinel down-after-milliseconds <名字> <毫秒>:多久收不到心跳回复,就认为主节点"主观下线"。这里设为 1000ms,是为了演示效果;实际默认是 30000ms。

细节:

  • 三份配置文件必须是三份独立的文件,不能共用一个。因为哨兵运行时会改写自己的配置文件(记录自己的 id、发现的从节点和其他哨兵等信息)。
  • 配置里只写了主节点,没有写从节点和其他哨兵,因为哨兵会自动发现:通过主节点的info命令得到所有从节点,再通过主节点上的发布订阅频道互相发现其他哨兵。
  • 说明:用容器名作为<ip>是演示的写法,Redis 6.2+ 需要额外配置resolve-hostnames yes才支持主机名,生产环境建议直接写 IP。

启动哨兵:

docker-composeup-d

2.3 模拟主节点故障

手动把主节点停掉:

dockerstop redis-master

主节点一挂,哨兵节点就开始工作了。在哨兵的目录下用docker-compose logs查看哨兵节点的工作情况:

日志里几个关键的事件,正好对应故障转移的过程:

日志事件含义
+sdown主观下线:这个哨兵认为主节点挂了
+odown客观下线:达到法定票数,确认主节点挂了
+try-failover开始尝试故障转移
+vote-for-leader/+elected-leader哨兵之间投票,选出 leader
+selected-slaveleader 选中了某个从节点作为新主
+promoted-slave该从节点已晋升为主节点
+switch-master主节点切换完成,通知客户端新主节点的地址

2.4 旧主节点恢复

再把原来的主节点启动起来,观察它的状态:

docker-composestart master redis-cli-p6379

可以发现,旧的主节点恢复后变成了从节点,挂在新的主节点下面,没有"抢回"主节点的位置。这也是哨兵自动完成的,不需要人工干预。

三、故障转移的原理

哨兵重新选择主节点,大致分三步:判定主节点挂了 → 选出 leader 哨兵 → leader 选出新的主节点。

3.1 主观下线与客观下线

  • 主观下线(SDOWN):一个哨兵给节点发的心跳包,在down-after-milliseconds内没有收到有效回复,这个哨兵就单方面认为该节点挂了。
  • 客观下线(ODOWN):主观下线只是一个哨兵的看法,可能误判。它会询问其他哨兵,认为主节点挂了的哨兵数量达到了法定票数(quorum),才算客观下线,才能开始故障转移。

这里的"挂"不一定是进程崩了,只要是无法正常访问,都可以视为是挂了(比如网络不通、进程卡死)。

3.2 选举 leader 哨兵

确认主节点客观下线后,需要由一个哨兵来执行故障转移,不能多个哨兵各干各的,所以要先选出 leader。

举个例子:1 号哨兵最先发现主节点客观下线,它就向其他哨兵发起投票请求,给自己投一票;另外两个哨兵也给 1 号哨兵投了一票,1 号哨兵得票过半,成为 leader。本质上是谁反应快、谁先发起,谁就更容易当选。(这里用的是 Raft 算法的简化版)

  • 细节:quorum只决定"能不能判定客观下线";真正执行故障转移,还需要这个 leader 得到==多数(超过哨兵总数一半)哨兵的授权==。这也是哨兵要部署奇数个的原因。

3.3 leader 如何挑选新的主节点

leader 会去从节点中挑选一个作为新的主节点,先排除掉不健康的节点(已经下线、断线太久的),剩下的按下面三个条件依次比较:

  1. 优先级:每个 Redis 数据节点在配置文件中都有一个优先级配置(replica-priority,默认 100),值越小优先级越高(设为 0 表示永远不会被选为主节点)。
  2. offset(复制偏移量):优先级相同,就看同步进度,offset 越大说明数据越新,越容易胜出。
  3. run id:前两项都一样,就看 run id,字典序最小的胜出。run id 是每个 Redis 节点启动时随机生成的一串字符,这里只是为了保证最终能选出唯一的节点。

为什么要看 offset?
因为主从复制是异步的,从节点的数据可能落后于旧主节点。选 offset 最大的,意味着选出的新主丢失的数据最少。

3.4 新主确定之后

  • leader 让被选中的节点执行slaveof no one,晋升为主节点。
  • leader 让其他从节点执行slaveof 新主节点,改认新的主节点。(默认一次只让 1 个从节点去同步,避免新主压力过大,由parallel-syncs配置)
  • 哨兵把新主节点的信息通知给客户端(+switch-master)。
  • 旧主节点恢复后,被设置为新主节点的从节点。

四、总结与局限

哨兵 =主从复制 + 自动故障转移,解决了主节点挂了需要人工处理的问题,提高了系统的可用性。但它依然有局限:

  • 写能力和容量没有提升:写操作依然只能在一个主节点上,每个节点保存的还是全量数据,受单机内存限制。
  • 切换期间有短暂的不可用:从主节点挂了,到新主节点选出来,这段时间内写操作是失败的。
  • 可能丢数据:主从复制是异步的,旧主节点上还没来得及同步给从节点的数据,在切换后会丢失。

要解决写能力和存储容量的问题,就需要把数据分片存储到多组节点上,这就是下一篇要讲的Redis 集群(Cluster)。

非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!🎉

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 12:25:34

无人机路径规划与导航Python实战:从算法到闭环控制

简介&#xff1a;本资源是一套基于Python实现的无人机路径规划与导航实战项目&#xff0c;面向人工智能、自动化及机器人方向的学习者与开发者&#xff0c;聚焦A 启发式搜索算法在真实场景中的工程落地。项目完整覆盖地图建模、节点状态管理、曼哈顿/欧氏距离启发式函数设计、…

作者头像 李华
网站建设 2026/10/16 12:25:14

PhotoImpact v13.0上手教程:兼容设置与批处理实战

简介&#xff1a;Ulead PhotoImpact v13.0是一款功能完备的图像编辑与图形处理软件&#xff0c;定位面向网页设计师、平面创作者及数码照片处理用户&#xff0c;解决从图像修饰、特效合成到动画输出的连续制作需求。软件集成快速克隆与编修工具、多类滤镜与填充效果、快速特效模…

作者头像 李华
网站建设 2026/10/16 12:24:16

码录集:Radzen 6.0.0 这几个 API 坑,我替你踩了

大家好&#xff0c;我是码农刚子。这是「Radzen 实战」第 1 篇&#xff0c;复盘 SPcbERP 里那些差点出事的真实坑。 SPcbERP 是 PCB 行业 ERP&#xff0c;前端用 Radzen.Blazor 搭。项目跑了小半年&#xff0c;一直稳。 上个月做依赖升级&#xff0c;把 Radzen.Blazor 从 5.x…

作者头像 李华
网站建设 2026/10/16 12:24:15

我合作了一家等离子表面处理设备厂家,记录一下选型过程

实验室要添一台等离子表面处理设备&#xff0c;我前后接触了几家厂家&#xff0c;最后合作的是深圳媛子智能有限公司。记录一下选型过程&#xff0c;给同样在选设备的朋友参考。第一步&#xff0c;查认证。很多厂家说“有CE”&#xff0c;但要证书编号就含糊。媛子智能直接给了…

作者头像 李华
网站建设 2026/10/16 12:22:47

第 2 章 Fluss 核心架构原理

第 2 章 Fluss 核心架构原理 章节定位:全系列理论地基。控制面 / 数据面、LogTablet / KvTablet、复制与恢复——后续每一章的"为什么"都要回到本章。 版本基线:Fluss 1.0.0 + Flink 2.3(connector fluss-flink-2.3-1.0.0)+ Paimon 2.0/1.4 + Doris 4.1 + Flink …

作者头像 李华