千家信息网

你管这叫哨兵?

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,本文来自微信公众号:低并发编程 (ID:dibingfa),作者:闪客我是一个苦逼的运维,有一次老板过来找我。老板:现在有四个 redis 节点摆在你面前,一主三从,你负责盯着点,主节点挂了你赶紧想办
千家信息网最后更新 2026年11月04日你管这叫哨兵?

本文来自微信公众号:低并发编程 (ID:dibingfa),作者:闪客

我是一个苦逼的运维,有一次老板过来找我。

老板:现在有四个 redis 节点摆在你面前,一主三从,你负责盯着点,主节点挂了你赶紧想办法拿从节点顶上来,交给你了!

这还不简单!

首先我先分别连上这四台 redis 节点。

redis-cli -h 10.232.0.0 -p 6379redis-cli -h 10.232.0.1 -p 6379redis-cli -h 10.232.0.2 -p 6379redis-cli -h 10.232.0.3 -p 6379

然后每隔 1s 分别发送 redis 专属的命令 PING

我就这样一直不断地发送着 PING 命令,日复一日。

终于有一天,发送给主节点的 PING 命令收到了无效回复!

我立刻打起了精神,开始操作了起来。

但我没有慌乱了手脚,很快我就梳理好了即将要做的三件事。

选择一个从节点,将其变为主节点。

选哪个节点好呢?先别管那么多了,随便选一个,就 10.232.0.3:6379 这个吧!

我对着这个节点,发送了一个命令。

10.232.0.3:6379 slaveof no oneOK

我想,这个节点应该就已经变成了主节点了,但我不太敢确定,于是又发送了一个命令进行确认。

10.232.0.3:6379 inforole:slave

诶,还没有变成主节点呢,那再给他点时间。一秒钟之后,我再次进行查看。

10.232.0.3:6379 inforole:master

嗯,这回已经成功变成主节点啦,进行下一步!

修改其他从节点的附属主节点

很简单,向另外两台从节点发送命令。

10.232.0.1:6379 slaveof 10.232.0.3 6379OK10.232.0.2:6379 slaveof 10.232.0.3 6379OK

将挂掉的主节点变为从节点

这一步充分体现了我多年的运维经验,很多人都想不到。

原来的主节点我可不能不管,万一他又复活了,就得乖乖成为新主节点的从节点。

10.232.0.0:6379 slaveof 10.232.0.3 6379

但是我不能直接发送这个命令给它,因为它还挂着呢,所以我将命令保存起来,只要它一复活我就发给它这个命令。

整个三步看起来是这个样子。

经过多次这样的操作,我终于熟悉了整个流程。

为了解放我自己的双手,我把这个固定的流程,写成了一个程序。

这个程序能实时监控这些 redis 节点的状态,并能自动报告并处理突发情况,我给他命名为哨兵程序。

而这个哨兵程序我单独用一台服务器部署,这个服务器就称为哨兵节点。

哨兵一开始就连接这 4 个 redis 节点,并持续我刚刚的操作过程。

优化

我还发现了一个小的优化点,我无需知道这 4 个节点的全部信息,只需要知道主节点即可。

从节点的信息,我通过向主节点发送 info 命令即可获取,而且可以不断获取来更新。

10.232.0.0:6379> info...role:master...slave0:ip=10.232.0.1,port=6379,state=online ...slave0:ip=10.232.0.2,port=6379,state=online ...slave0:ip=10.232.0.3,port=6379,state=online ......

这样,我在启动哨兵时,只要知道主节点即可,而且这样获取的从节点信息更准确,也更实时,就不用一直问老板啦。

虽然已经可以解放双手,但兴致来了的我仍然没有收手。

刚刚主节点挂了之后,我随机从三个从节点中选择了一个作为主节点,不妨让这个随机也智能一些吧,不然总觉得太 low。

首先,我把所有的从节点的主要信息列出来(这里假设多一些节点方便分析)

节点

状态

距离上次回复的时间

复制偏移量

uid

1

DISCONNECTED

8

50

12345

2

DOWN

8

50

12346

3

√

7

50

12347

4

√

1

50

12348

5

DOWN

8

50

12349

6

√

1

50

12350

先去掉所有断线或下线的节点。

节点

状态

距离上次

回复的时间

复制偏移量

uid

1

DISCONNECTED

8

50

12345

2

DOWN

8

50

12346

3

√

7

50

12347

4

√

1

50

12348

5

DOWN

8

50

12349

6

√

1

50

12350

再去掉最后一个 ping 请求过去后,未回应的时间大于 5s 的。

节点

状态

距离上次

回复的时间

复制偏移量

uid

1

DISCONNECTED

8

50

12345

2

DOWN

8

50

12346

3

√

7

50

12347

4

√

1

50

12348

5

DOWN

8

50

12349

6

√

1

50

12350

剩下两个,是至少状态健康的节点,继续择优录取。

我们比较其复制偏移量的值,这个代表其从主节点成功复制了多少数据,选择一个复制偏移量最多的,也就是与主节点最接近同步的。

节点

状态

距离上次

回复的时间

复制偏移量

uid

4

√

1

50

12348

6

√

1

50

12350

不过我们发现其偏移量一样。

到现在,这两个节点无论从健康状态,还是同步状态,都是完全一样的,没办法分出谁好谁坏了,那怎么办呢?

没关系,还有一个终极武器,就是唯一标识 uid,这两个 uid 在启动节点时就保证了必然不相同,我们选择一个相对较小的。

节点

状态

距离上次

回复的时间

复制偏移量

uid

4

√

1

50

12348

OK,最终可以唯一确定一个从节点,就把它变为主节点了!

我把这个复杂的过程,写成了一个方法,sentinelSelectSlave(),放在了哨兵程序中,用来选择一个从节点。

嗯,现在这个程序看起来,已经很完善了!

我放心地把这个哨兵程序启动起来,之后的很长一段时间,我就靠着我的哨兵程序,成功自动应对了很多次突发情况,有一次甚至在半夜两点多迅速将问题发现并解决。

老板一直夸我坚守岗位,半夜了还这么负责,我很快得到了晋升。

直到有一次,我正在开开心心摸鱼,老板气哄哄地走来。

老板:redis 都挂了一个小时了!你怎么还不处理!额?你这是看什么?leetcode?是准备跳槽了么!

我一脸懵逼,赶紧看了一下我的哨兵进程,我擦,哨兵服务器挂掉了!

我被降了职,但仍然要负责看着这些 redis 节点,这回我可不敢怠慢了。

我继续用哨兵程序监控着这些节点的生死,但我自己又多了一项任务,就是监控哨兵节点的状态,仿佛一夜回到解放前。

怎么样再次解放我的双手,让程序帮我去监控和处理这个哨兵节点的健康状态呢?

我灵机一动,部署多个哨兵节点,成为哨兵集群!只要有一个节点活着就行,这样同时都挂掉的概率就非常小了。

当然,有三个哨兵时,每个哨兵就不能太自我了,得听从组织统一安排。

主客观问题

比如说,当哨兵 1 认为主节点已经挂掉时,不能认为主节点就真的挂掉了,这种判断叫做主观下线。

哨兵 1 主观认为主节点下线时,需要询问其他节点,主节点是否已经下线。

如果其中哨兵 2 回复,主节点下线了,哨兵 3 回复,主节点没下线。

那么这个时候,哨兵集群中,一共有 2 个哨兵都主观认为主节点下线。

当主观下线的数量达到一定值时,比如说 >=2 时,我们就可以认为,主节点客观下线。

一旦主节点客观下线了,那就又可以走之前的故障处理流程,即选择一个从节点变成主节点。

领头问题

接下来,将从节点变成主节点,也就是后续的这个故障处理流程,由哪个哨兵来完成呢?

总不能同时来操作吧。

那就必然需要选举出一个领头来完成这个事。

怎么选举出一个领头呢?我总不能再用一个哨兵去做吧,那样就无限套娃了,最好的方式就是让他们仨自发地决定。

这部分有点复杂,在这里展开不太合适,可以单独水一篇文章来讲解,感兴趣的同学可以看一下 Raft 算法,哨兵集群正是通过这个算法来选举领头的。

OK,我终于再次解放了我的双手!

我把这个破玩意,称为哨兵系统,或者哨兵集群!

我再给哨兵起个英文名字,叫 Sentinel 吧!

后记

本次选取的 redis 代码为 redis-3.0.0。

之所以能够通过 "我" 这个视角来写哨兵,正是因为哨兵这个程序,完全可以由人不断输入 redis 命令来轻松完成,并不需要什么其他协议的支持。

比如判断节点健康状态的 ping,拿到节点信息的 info,设置主从节点的 slaveof,甚至询问其他哨兵节点是否在线的命令 sentinel is-master-down-by addr 等等,都是 redis 支持的客户端命令,对用户端非常友好。

redis 的源代码也是非常干净,而且设计得很精妙,建议有兴趣的读者可以深入源码进行阅读,不算难。

比如上面讲的,如何从一堆从节点中,选取一个作为主节点。

这个知识点网上搜,你会搜到很多云里雾里的解释,而如果你看源码,你会发现这个过程非常清晰。

sentinelRedisInstance *sentinelSelectSlave() {    ...    // 去掉一些节点    while((de = dictNext(di)) != NULL) {        ...        if (slave->flags & (DOWN||DISCONNECTED)) continue;        if (mstime() - slave->last_avail_time > 5000) continue;        if (slave->slave_priority == 0) continue;        if (...) continue;        ...    }    // 剩下的节点排个序    qsort(..., compareSlavesForPromotion);    // 取第一个    return instance[0];}// 怎么排序呢?就这么排int compareSlavesForPromotion(const void *a, const void *b) {    // 先按优先级排    if ((*sa)->slave_priority != (*sb)->slave_priority)        return (*sa)->slave_priority - (*sb)->slave_priority;    // 优先级一样按偏移量排    if ((*sa)->slave_repl_offset > (*sb)->slave_repl_offset) {        return -1;    } else if ((*sa)->slave_repl_offset < (*sb)->slave_repl_offset) {        return 1;    }    ...    // 偏移量一样按唯一标识排    return strcasecmp(sa_runid, sb_runid);}

我想相信如果你停下来仔细看几秒,哪怕你对 c 语言并不熟悉,也能看懂个大概了,再结合网上或者书上关于这块的描述,你就有了很直观的印象。

关于 redis 源码的深入学习,我建议先阅读黄健宏的《Redis 设计与实现》,这本书代码量很少,但逻辑描述完全按照写代码的思维来讲,你读一下就知道了。

读完这本书,直接上手 redis 源码的阅读,你可以选择 redis-1.0.0 代码,非常少,主要阅读其整个网络 IO 以及命令处理的流程。

接着,从 redis-3.0.0 开始,有针对性研究其主从、集群、哨兵等特性。

这样,redis 在你这,就不再是模模糊糊了。

节点 哨兵 命令 状态 程序 下线 偏移 时间 选择 老板 处理 信息 流程 集群 解放 健康 主观 代码 双手 源码 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 喔噻互联网科技有限公司 华服互联网科技怎么样 高科技互联网 以房互联网科技 世界互联网领先科技成果 融贸通互联网科技 互联网 新科技 四川狮子座互联网科技 互联网科技公司名称 天扬科技互联网广告 四个字互联网科技公司取名 太原新华互联网科技学校怎么样 武汉健翔互联网络科技有限公司 洛阳昕诺飞互联网科技有限公司 世界互联网领先科技成果获奖 杭州披云加互联网科技有限公司 互联网风格科技公司名称 互联网科技机房 北京品多多互联网科技有限公司 广东猫王互联网科技有限公司电话 互联网核心科技是什么 凡科互联网科技 融势互联网科技 盛创互联网科技有限公司 南山科技园互联网公司有哪些 互联网与医疗科技 华中科技大学互联网的牛人 山东大美朱楼互联网科技有限公司 上海乐五互联网科技有限公司 宁波允成互联网科技有限公司
0