哨兵模式
该文章来源于知乎的https://zhuanlan.zhihu.com/p/516715720
哨兵模式是主从的升级版,因为主从的出现故障后,不会自动恢复,需要人为干预,这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
因此哨兵模式有以下的特点
1.【监控】:监控master和slave是否正常运行,以及哨兵之间也会相互监控
2. 【自动故障恢复】:当master出现故障的时候,会自动选举一个slave作为master顶上去。
3. 【通知】:哨兵可以把Redis实例的运行故障信息通过API通知监控系统或者其他应用程序。
4. 【配置中心】:哨兵可以作为客户端服务发现的授权源,客户端连接到哨兵请求给定服务的Redis主节点地址。如果发生故障转移,哨兵会通知新的地址。这里要注意:哨兵并不是Redis代理,只是为客户端提供了Redis主从节点的地址信息。
哨兵模式的监控配置信息,是通过配置从数据库的sentinel monitor <master-name> <ip> <redis-port> <quorum>
来指定的,比如:
// mymaster 表示给master数据库定义了一个名字,后面的是master的ip和端口,1表示至少需要一个Sentinel进程同意才能将master判断为失效,如果不满足这个条件,则自动故障转移(failover)不会执行
sentinel monitor mymaster 127.0.0.1 6379 1
哨兵机制如何工作
哨兵其实是一个运行在特殊模式下的redis进程,所以它也是一个节点。从“哨兵”这个名字也可以看出,它相当于是“观察者节点”,观察的对象是主从节点。
当然,它不仅仅是观察那么简单,在它观察到有异常的状况下,会做出一些“动作”,来修复异常状态。
哨兵节点主要负责三件事情:监控,选主,通知。
所以接下来的重点就是
- 哨兵节点是如何监控节点的?又是如何判断主节点是否真的故障了?
- 根据什么规则选择一个从节点切换为主节点?
- 怎么把新主节点的相关信息通知给从节点和客户端呢?
监控–故障判断
哨兵会每隔 1 秒给所有主从节点发送 PING 命令,当主从节点收到 PING 命令后,会发送一个响应命令给哨兵,这样就可以判断它们是否在正常运行,这就是“心跳机制”。
如果主节点或者从节点没有在规定的时间内响应哨兵的PING命令,哨兵就会将它们标记为【主观下线】。这个【规定时间】是配置项down-after-milliseconds
参数设定的,单位是毫秒。
同样还存在客观下线,但只适用于主节点。如何判定主节点为【客观下线】呢?
当一个哨兵判断主节点为【主观下线】后,就会向其他哨兵发起命令,其他哨兵收到这个命令后,就会根据自身和主节点的网络状况,做出赞成投票或者拒绝投票的响应。
当这个哨兵的赞同票数达到哨兵配置文件中的quorum
配置项设定的值后,这时主节点就会被该哨兵标记为【客观下线】。
例如,现在有 3 个哨兵,quorum 配置的是 2,那么一个哨兵需要 2 张赞成票,就可以标记主节点为“客观下线”了。这 2 张赞成票包括哨兵自己的一张赞成票和另外两个哨兵的赞成票。
PS:quorum 的值一般设置为哨兵个数的二分之一加1,例如 3 个哨兵就设置 2。
哨兵判断完主节点客观下线后,哨兵就要开始在多个「从节点」中,选出一个从节点来做新主节点。
之所以针对【主节点】设计【主观下线】和【客观下线】两个状态,是因为有可能【主节点】其实并没有故障,可能只是因为主节点的系统压力比较大或者网络发送了拥塞,导致主节点没有在规定时间内响应哨兵的 PING 命令。
所以,为了减少误判的情况,哨兵在部署的时候不会只部署一个节点,而是用多个节点部署成哨兵集群(最少需要三台机器来部署哨兵集群),通过多个哨兵节点一起判断,就可以就可以避免单个哨兵因为自身网络状况不好,而误判主节点下线的情况。同时,多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,误判率也能降低。
主从故障转移
前面说过,为了更加“客观”的判断主节点故障了,一般不会只由单个哨兵的检测结果来判断,而是多个哨兵一起判断,这样可以减少误判概率,所以哨兵是以哨兵集群的方式存在的。
问题来了,由哨兵集群中的哪一个节点进行主从故障转移呢?哪个哨兵节点判断主节点为「客观下线」,这个哨兵节点就是候选者
举个例子,假设有三个哨兵。当哨兵 A 先判断到主节点「主观下线后」,就会给其他实例发送 is-master-down-by-addr 命令。接着,其他哨兵会根据自己和主节点的网络连接情况,做出赞成投票或者拒绝投票的响应。
当哨兵A收到赞成票数达到哨兵配置文件中的quorum配置项设定的值后,就将主节点标记为【客观下线】,此时的哨兵A就是一个Leader候选者。
那么候选者如何成为Leader?
候选者会想其他哨兵发送命令,表明希望成为Leader来执行主从切换,并让所有其他哨兵对它进行投票
每个哨兵只有一次投票机会,如果用完后就不能参与投票了,可以投给自己或投给别人,但是只有候选者才能把票投给自己。
那么在投票过程中,任何一个【候选者】都满足两个条件
- 第一,拿到半数以上的赞成票
- 第二,拿到的票数同时还需要大于等于哨兵配置文件中的
quorum
值
举个例子,假设哨兵节点有3个,quorum设置为2,那么任何一个想成为Leader的哨兵只要拿到2张赞成票,就可以选举成功了。如果没有满足条件,就需要重新进行选举。
这时候有的同学就会问了,如果某个时间点,刚好有两个哨兵点判断到主节点为客观下线,那这时不就有两个候选者了?这时该如何决定谁是leader呢?
每位候选者都会先给自己投一票,然后向其他哨兵发起投票请求。如果投票者先收到【候选者A】的投票请求,就会先投票给他,如果投票者用完投票机会后,收到【候选者B】的投票请求后,就会拒绝投票。这时,候选者A先满足了上面的那两个条件,所以【候选者A】就会被选举为Leader
为什么哨兵节点至少要有3个?
如果哨兵集群中只有2个哨兵节点,此时如果一个哨兵想要成功成为leader,就必须获得2票,而不是一票。
所以,如果哨兵集群中有一个哨兵挂掉了,那么就只剩下一个哨兵了,如果这个哨兵想成为Leader,这时票数就无法达到2票,就无法成功成为leader,这时是无法进行主从节点切换的。
因此,通常我们至少会配置3个哨兵节点。这时,如果哨兵集群中有个哨兵挂掉了,那么还剩下两个哨兵,如果这个哨兵想要成为leader,这时还是有机会达到两票的,所以还是可以通过选举成功的,不会导致无法进行主从节点切换
当然,你要问,如果3个哨兵节点,挂掉2个怎么办?这个时候得人为介入了,或者增加多一点哨兵节点
再说一个问题,Redis1主4从,5个哨兵,quorum设置为3,如果2个哨兵故障,当主节点宕机时,哨兵能否判断主节点“客观下线”?主从能否自动切换?
- 哨兵集群可以判定主节点“客观下线”。 哨兵集群还剩下3个哨兵,当一个哨兵判断主节点“主观下线”后,询问另外2个哨兵后,有可能拿到3张赞同票,这时就达到了quorum的值,因此,哨兵集群可以判定主节点为“客观下线”。
- 哨兵集群可以完成主从切换。 当有个哨兵标记主节点为【客观下线】后,就会进行选举Leader的过程,因为此时哨兵集群还剩下3个哨兵,那么还是可以拿到半数以上(5/2+1=3)的票,而且也达到了quorum值,满足了选举Leader的两个条件,所以就能选举成功,因此哨兵集群可以完成主从切换。
如果quorum设置为2,并且如果有三个哨兵故障的话。此时哨兵集群还是可以判断主节点为“客观下线”,但哨兵不能完成主从切换了。
如果quorum设置为3,并且如果有3个哨兵故障的话,虽然可以判定主节点为“客观下线”,但是不能完成主从切换,这感觉【判定主节点为客观下线】这件事白做了一样,既然这样,还不如不做,quorum为3的时候,就可以避免这种无用功。
所以,quorum的值建议设置哨兵个数的二分之一加1, 例如3个哨兵就设置2,5个哨兵设置为3,而且哨兵节点的数量应该是奇数
主从故障转移的过程是怎么样的
在哨兵集群中通过投票的方式,选举出哨兵leader后,就可以进行主从故障的过程了,如下图:
主从故障冠以操作包含以下四个步骤:
- 第一步:在已下线主节点(旧节点)属下的所有【从节点】里面,挑选出一个从节点,并将其转换为主节点
- 第二步:让已下线主节点属下的所有【从节点】修改复制目标,修改为复制【新主节点】
- 第三步:将新主节点的ip地址和信息,通过【发布者/订阅者机制】通知给客户端
- 第四步:继续监视旧节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点
步骤一:选出新主节点
故障转移操作第一步要做的就是在已下线主节点属下的所有【从节点】中,挑选出一个状态良好、数据完整的从节点,然后向这个从节点发送SLAVEOF no one命令,将这个【从节点】转换为【主节点】。
那么多【主节点】,到底选择哪一个从节点作为新主节点的?
随机选取的方式可以吗?随机选取的方式虽然看是简单,但是选取了一个网络状态差的节点做为新主从节点,那么可能在将来不久就会又进行主从故障转移。
所以,我们首先就是要先排除网络状态差的节点。
怎么判断从节点之前的网络状态不好呢?
Redis有个 down-after-milliseconds * 10
配置项,其down-after-milliseconds
是主从节点断连的最大连接超时时间。如果在down-after-millisecondes
毫秒内,主从节点都没有通过网络联系上,我们就可以认为主从节点断连了。,如果发生断连的次数超过了10次,说明这个从节点的网络状态不好,不适合作为新主节点
至此,我们就把网络状态不好的从节点过滤掉了,接下来要对所有从节点进行三轮考察:优先级、复制进度、ID号。 在进行每一轮考察的时候,哪个从节点优先胜出,就选择其作为新主节点。
- 第一轮考察:哨兵首先会根据从节点的优先级来进行排序,优先级越小,排名越靠前
- 第二轮考察:如果优先级相同,则查看复制的下标,哪个从【主节点】接收的复制数据多,哪个就靠前。
- 第三轮考察:如果优先级和下标都相同,就选择从节点ID较小的那个。
第一轮考察,优先级最高的从节点胜出
Redis 有个叫 slave-priority
配置项,可以给从节点设置优先级。
每一台从节点的服务器配置不一定是相同的,我们可以根据服务器性能配置来配置从节点的优先级。
比如,如果【A从节点】的为例内存是所有从节点中最大的,那么我们可以把【A从节点】的优先级设置成最高。这样当哨兵进行第一轮考虑的时候,优先级最高的A从节点就会优先胜出,于是就会成为新主节点。
第二轮考察:复制进度最靠前的从节点胜出
如果第一轮考察中,发现优先级最高的从节点有两个,那么就会进行第二轮考察,比较两个从节点的幅值进度。
什么是复制进度?
主从架构中,主节点会将写操作同步给从节点,在这个过程中,从节点会用master_repl_offset记录当前的最新写操作在repl_backlog_buffer中的位置(如下图中的【主服务器已经写入的数据】的位置),而从节点会用slave_repl_offset这个值记录当前的复制进度(如下图中的【从服务器要读的位置】的位置)
如果某个从节点的slave_tepl_offset最接近master_repl_offset,说明它的复制进度是最靠前的,于是选它作为新主节点
第三轮考察;ID号小的节点胜出
如果在第二轮考察中,发现有两个从节点优先和复制进度都是一样的,那么就会进行第三轮考察,比较两个从节点ID号,ID号小的从节点胜出
什么是ID号
每个节点都有一个编号,这个编号就是ID号,用来唯一识别从节点的。
到这里,选主的事情终于结束了。主要流程如下图所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Edh7WQSR-1678332409191)(:/3f3630f22eb7484586d11799d4d1f421)]
在选举出从节点后,哨兵leader向北选中的从节点发送SLAVEOF no one
命令,让这个从节点解除从节点的身份,将其变为新主节点
如下图,哨兵Leader向被选中的从及诶单server2发送SLAVEOF no one
命令,将该从节点升级为新主节点。
在发送SLAVEOF no one
命令之后,哨兵leader会以每秒一次的频率想被升级的从节点发送info
命令(没有进行故障转移之前,info命令的频率是每10秒一次),并观察命令回复中的角色信息,当被升级节点的角色信息从原来的slave变为master时,哨兵leader就知道被选中的从节点已经顺利升级为主节点了。
如下图,选中的从节点server2升级为新主节点
步骤二:将从节点指向新主节点
当新主节点出现之后,哨兵leader下一步要做的就是,让已下线主节点属下的所有【从节点】指向【新主节点】,这一动作可以通过向【从节点】发送slaveof
命令实现。
如下图,哨兵leader向所有节点(server3和server4)发送SLAVEOF
,让它们成为新主节点的从节点。
所有从节点指向新主节点后的拓扑图如下:
步骤三:通知客户的主节点已更换
经过前面的一系列操作后,哨兵集群终于完成主从切换的工作,那么新主节点的信息要如何给客户端呢?
这主要通过Redis的发布者/订阅者机制来实现的。每个哨兵提供发布者/订阅者机制,客户端可以从哨兵订阅消息。
哨兵提供的消息订阅频道有很多,不同频道包含了主从节点切换过程中的不同关键时间,几个常见的事件如下:
客户端和哨兵建立连接后,客户端会订阅哨兵提供的频道。主从切换完成后,哨兵就会向+switch-master
频道发布新主节点的ip地址和端口的信息,这个时候客户端就可以收到这条信息,然后用这里的新主节点ip地址和端口进行通信。
通过发布者/订阅者机制,有了这些事件通知,客户端不仅可以在主从切换后得到新主节点的连接信息,还可以监控到主从节点切换过程中发生的各个重要事件。这样,客户端就可以智岛主从切换到哪一步,有助于了解切换进度。
步骤四:将旧节点变为从节点
故障转移操作最后要做的是,继续监视旧主节点,当旧主节点重新上线时,哨兵集群就会向它发送SLAVEOF
命令,让它成为新主节点的从节点,如下图:
至此,整个主从节点的故障转移工作结束
哨兵集群是如何组成的
前面提到了Redis的发布者/订阅者机制,那就不得不提一下哨兵集群的组成方式,因为它也用到了这个技术。
在我第一次搭建哨兵集群的时候,当时觉得很诧异。因为在配置哨兵的信息时,竟然只需要填下面这几个参数,设置主节点名字,主节点的IP地址和端口号以及quorum值。
sentinel monitor <master-name> <ip> <redis-port> <quorum>
不需要填其他哨兵节点的信息,那么它们是如何感知对方的,又是如何组成哨兵集群的?
哨兵之间通过Redis的发布者/订阅者机制来相互发现的。
在主从集群中,主节点上有一个名为__sentinel__:hello
的频道,不同的哨兵就是通过它来相互发现,实现互相通信的。
在下图中,哨兵A把自己的IP地址和端口信息发送到__sentinel__:hello
频道上,哨兵B和C订阅了该频道。那么此时,哨兵B和C就可以从这个频道直接获取哨兵A的IP地址和端口号。然后,哨兵B和C可以和哨兵A建立网络连接。
通过这个方式,哨兵B和C也可以建立网络连接,这样一来,哨兵集群就形成了。
哨兵集群会对【从节点】的运行状态进行监控,那哨兵集群如何知道【从节点】的信息?
主节点知道所有【从节点】的信息,所以哨兵会每10s一次的频率想主节点发送INFO命令来获取所有【从节点】的信息
如下图所示,哨兵B给主节点发送INFO命令,主节点接受到这个命令后,就会把从节点列表返回给哨兵。接着,哨兵就可以根据从节点列表中的连接信息,和每一个从节点建立连接,并在这个连接上持续地对节点进行监控。哨兵A和C可以通过相同的方法和从节点建立连接。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ma8NFy6h-1678332409193)(:/cb3f1d98d6534c9483e5e1fcbf8699db)]
正式通过Redis的发布者/订阅者机制,哨兵可以相关感知,然后组成集群,同时,哨兵又通过INFO命令,在主节点里获得了所有从节点连接信息,于是就能和从节点建立连接,并进行监控了。
总结
Redis在2.8版本以后提供的Redis机制,它的作用是实现主从节点故障转移。他会检测主节点是否存活,如果发现主节点挂了,它会选举一个从节点切换为主节点,并且把新主节点的相关信息通知给从节点和客户端。
哨兵一般以集群的方式部署,至少需要三个哨兵节点,哨兵集群主要负责三件事:监控,选主,通知
哨兵节点通过Redis的发布者/订阅者机制,哨兵之间可以相互感知,相互连接,然后组建成哨兵集群,同时哨兵又通过INFO命令,在主节点里获得了所有从节点连接信息,于是就能和从节点建立连接,并进行监控了。
- 第一轮投票:判断主节点下线
当哨兵集群中的某个哨兵判定主节点下线(主观下线)后,就会向其他哨兵发起命令,其他哨兵收到这个命令后,就会根据自身和主节点的网络状况,做出赞成投票或者拒绝投票的响应。
当这个哨兵的赞同票数达到哨兵配置文件中的 quorum 配置项设定的值后,这时主节点就会被该哨兵标记为【客观下线】。 - 第二轮投票:选出哨兵leader
某个哨兵判定主节点客观下线后,该哨兵就会发起投票,告诉其他哨兵,它想成为 leader,想成为 leader 的哨兵节点,要满足两个条件:
- 第一,拿到半数以上的赞成票;
- 第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。
- 由哨兵leader进行主从故障转移
选举出了哨兵 leader 后,就可以进行主从故障转移的过程了。该操作包含以下四个步骤:
- 第一步:在已下线主节点(旧主节点)属下的所有【从节点】里面,挑选出一个从节点,并将其转换为主节点,选择的规则:
(1)过滤掉已经离线的从节点;
(2)过滤掉历史网络连接状态不好的从节点;
(3)将剩下的从节点,进行三轮考察:优先级、复制进度、ID 号。在每一轮考察过程中,如果找到了一个胜出的从节点,就将其作为新主节点。 - 第二步:让已下线主节点属下的所有【从节点】修改复制目标,修改为复制【新主节点】;
- 第三步:将新主节点的 IP 地址和信息,通过【发布者/订阅者机制】通知给客户端
- 第四步:继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点