Redis哨兵机制详细解读

目录

一.哨兵机制基本解读

1.哨兵机制的基本流程

1.1哨兵监控:

1.2自动切换主库流程

2.主库是否下线判断

2.1.主观下线

2.2客观下线

3.主从切换机制:主库下线了就要进行主从切换了

3.1筛选条件

3.2打分规则

3.3主从切换总结

4.哨兵机制总结

二.哨兵挂了

1.基于pub/sub机制的哨兵集群的组成

1.1哨兵之间互相被发现过程:

1.2哨兵消息互通说明:

1.3哨兵与从库建立连接:INFO命令

1.4哨兵与客户端间的信息同步

1.5基于pub/sub机制的客户端事件的通知

2.哪个哨兵执行主从切换?

2.1从哨兵选主,主要过程开始说起:

2.2哨兵选出Leader过程:Leader选举

3.总结:哨兵关键机制(哨兵联系主从客哨兵,Leader选主)

3.1哨兵集群的这些关键机制:

三.哨兵全部总结

1.哨兵怎么跟主库连接

2.哨兵怎么给从库发消息

3.哨兵怎么跟客户端联系


一.哨兵机制基本解读

主库发生故障了,如何不间断的服务?

哨兵模式:有效的解决主从库自动切换的关键机制

在Redis中如果从库发生故障了,客户端可以继续向主库和其他从库发消息,进行相关操作。但是如果主库故障了,直接影响到从库的同步操作(从库没有相应主库进行相关数据复制操作了)和没有实例来支持客户端进行写操作。

把从库切换成主库,需要涉及三个问题:

  1. 主库真的挂了吗?
  2. 应该选择哪一个从库来切换成主库?
  3. 怎么把新主库的信息通知给从库和客户端呢?

1.哨兵机制的基本流程

哨兵机制,在主从库实例运行的时候,就在运行。哨兵机制主要的行为就是监控。

1.1哨兵监控:

哨兵进程在运行过程中会周期性的给所有主从库发送PING命令,检测他们是否还在线运行。如果从库没有在规定时间内响应哨兵的PING命令,哨兵就会把它标记为“下线状态”。同样,如果主库没有在规定时间内响应哨兵的PING命令,哨兵也会把主库标记为“下线状态”,然后开始自动切换主库的流程。

1.2自动切换主库流程

  1. 这个流程首先执行的是哨兵的第二个任务:选主。主库挂了之后,哨兵需要从众多从库中,按照一定规则选择一个从库实例,把它作为更新的主库。这一步完成后,集群里就会有新的主库了。
  2. 然后执行的是最后一个任务:通知。在执行任务通知时,哨兵会把新主库的信息发送给其他所有从库,让他们执行replicaof命令,和新主库建立好连接,并且进行数据复制。同时,哨兵会把新主库连接信息给客户端,让他把新的请求发送到新主库上。

在这三个任务中通知任务相对简单,只需要给从库和客户端发送新主库的信息,让他们跟新主库连接就行,并不涉及决策逻辑。但是在监控和选主这两个任务中,哨兵需要作出两个决策:

  • 在监控任务中,哨兵需要决策主库是否处于下线状态
  • 在选主任务中,哨兵也要决定选取那个从库实例作为新主库。

如何判断主库是否下线?

2.主库是否下线判断

主库是否下线判断分为两种“主观下线”和”客观下线“。

2.1.主观下线

主观下线:哨兵机制会给主从库发送PING命令,检测他自己和主从库的网络连接情况,用来判断实例的状态。如果哨兵发现主从库的响应时间超时了,就会给主从库标记为“主观下线”。

误判

但是会出现一种情况,哨兵误判了,主从库并没有故障,主库也并没有下线,哨兵误判他下线了。误判一般发生在:集群网络压力大,网络拥塞,或者主库本身压力较大的情况下

一旦哨兵判断主库下线了,就需要进行选主,主从库同步等一系列操作,就会增加额外的计算和通信开销。

所以需要减少误判。

怎么减少误判呢?

引入多几个哨兵进行协商判断,也就是哨兵集群。

哨兵集群

哨兵集群:通常由多个实例组成的集群模式进行部署。引入多个哨兵实例进行判断,避免单个哨兵因为网络不好造成的误判主库下线的情况。多个哨兵网络同时不稳定的概率小,同时误判的概率也小了。

2.2客观下线

在判断主库是否下线时,不能由一个哨兵说了算,要大多数哨兵,都判断主库已经“主观下线”,主库才会标记为”客观下线“,这个说法表明主库下线已经成为了客观事实,判断原理就是:少数服从多数。

客观下线:当有N个集群时,当有N/2+1个集群已经判断主库“主观下线”,才能最终判断主库“客观下线”。减少误判带来的主从库切换开销。(有几个实例来作出主库“主观下线”的判断才可以,由Redis管理员自信设定)。

3.主从切换机制:主库下线了就要进行主从切换了

如何选定新主库?

一般来说,哨兵选择新主库的过程可以称为“筛选+打分”。就是在多个实例从库中,按照一定的筛选条件,把不符合条件的从库去掉。然后在按照一定的规则,给剩下的从库逐个打分,将得分最高的从库选为新主库。

3.1筛选条件

不仅仅要判断从库当前的状态,还要判断他之前的网络连接状态。如果之前从库跟主库断联次数超过了阈值,那么有有理由详细这个从库的网络连接状态不是很好,可以把它筛选掉。虽然现在他在运行,但是万一过一会断掉了,就需要重新选主,所以需要判断她之前的状态。

具体怎么判断呢?

使用配置项down-after-milliseconds*10。其中,down-after-milliseconds是我们认定是从库断联的最大时间。如果在down-after-milliseconds毫秒内,主从节点都没有网络连接上,那么就认为从库断联了。如果断联时间超过了10次就认为,这个从库的网络转态不是很好,不适合做主库。

3.2打分规则

可以分别按照三个规则进行打分:从库优先级、从库复制进度、以及从库ID号

只需要在某一轮得分最高,那么他就是新主库,选主过程到此结束,要是没有出现得分最高的,那么久进行下一轮。

第一轮:优先级slave-priority最高的从库得分高

用户可以通过slave-priority配置项,给不同的从库设置不同优先级。比如,你有两个从库,它们的内存大

小不一样,你可以手动给内存大的实例设置一个高优先级。在选主时,哨兵会给优先级高的从库打高分,如果有一个从库优先级最高,那么它就是新主库了。如果从库的优先级都一样,那么哨兵开始第二轮打分。

第二轮:和旧主库同步程度最接近的从库得分高

如果选择和旧主库同步最接近的从库作为主库,新主库上边就会有最新的数据。

如何判断从库和主库的同步进度呢?(复制进度)

主从库同步的时有个命令传播的过程,在这个过程中,主库会用master-repl-offset记录,当前的最新写操作在repl-backlog-buffer的中位置,而从库会用slave-repl-offset记录复制的进度。

所以我们需要找到master-repl-offset,slave-repl-offset最接近的从库。得分就高,就会被选为新主库。如果slave-repl-offset相同,就会进行下一轮的打分。

就像下图所示,旧主库的master_repl_offset是1000,从库1、2和3的slave_repl_offset分别是950、990和900,那么,从库2就应该被选为新主库。

第三轮:ID号小的从库得分高

每个实例都会有一个id,这个ID类似于从库的编号。Redis选主时,有一个规定:在优先级和复制进度相同的情况下,ID越小的得分越高。

3.3主从切换总结

首先哨兵机制会根据在线状态,网络状态,过滤筛选掉一部分不符合要求的从库。然后按照优先级,复制进度,ID大小对从库进行打分,得分最高的选为新主库。

4.哨兵机制总结

主从集群数据同步,保证了数据的可靠性。主库发生故障时,自动的主从切换是服务不间断的关键支撑。

Redis的哨兵机制自动完成了以下三大功能,从而实现了主从库的自动切换,可以降低Redis集群的运维开销:

  • 监控主库运行状态,并判断主库是否客观下线;
  • 在主库客观下线后,选取新主库;
  • 选出新主库后,通知从库和客户端。

为了降低误判率,在实际应用时,哨兵机制通常来用多实例的方式进行部署,多个哨兵实例通过“少数服从多数”的原则,来判断主库是否客观下线。一般来说,我们可以部署三个哨兵,如果有两个哨兵认定主库“主观下线”,就可以开始切换过程。当然,如果你希望进一步提升判断准确率,也可以再适当增加哨兵个数,比如说使用五个哨兵。

哨兵集群中有实例挂了,怎么办,会影响主库状态判断和选主吗?

简单说结论:存在故障节点时,只要集群中大多数节点状态正常,集群依旧可以对外提供服务

哨兵集群多数实例达成共识,判断出主库“客观下线”后,由哪个实例来执行主从切换呢?

哨兵集群判断出主库“主观下线”后,会选出一个“哨兵领导者”,之后整个过程由它来完成主从切换。

哨兵在操作主从切换的过程中,客户端能否正常地进行请求操作?

如果客户端使用了读写分离,那么读请求可以在从库上正常执行,不会受到影响。但是由于此时主库已经挂了,而且哨兵还没有选出新的主库,所以在这期间写请求会失败,失败持续的时间=哨兵切换主从的时间+客户端感知到新主库的时间.

如果不想让业务感知到异常,客户端只能把写失败的请求先缓存起来或写入消息队列中间件中,等哨兵切换完主从后,再把这些写请求发给新的主库,但这种场景只适合对写入请求返回值不敏感的业务,而且还需要业务层做适配,另外主从切换时间过长,也会导致客户端或消息队列中间件缓存写请求过多,切换完成之后重放这些请求的时间变长。

哨兵检测主库多久没有响应就提升从库为新的主库,这个时间是可以配置的(down-after-milliseconds参数)。配置的时间越短,哨兵越敏感,哨兵集群认为主库在短时间内连不上就会发起主从切换,这种配置很可能因为网络拥塞但主库正常而发生不必要的切换,当然,当主库真正故障时,因为切换得及时,对业务的影响最小。如果配置的时间比较长,哨兵越保守,这种情况可以减少哨兵误判的概率,但是主库故障发生时,业务写失败的时间也会比较久,缓存写请求数据量越多。

二.哨兵挂了

如果有哨兵实例在运行时发生了故障,主从库还能正常切换吗?

实际上,一旦多个实例组成了哨兵集群,即使有哨兵实例出现故障挂掉了,其他哨兵还能继续协作完成主库切换的工作,包括判定主库是不是处于下线状态,选择新主库,以及通知从库和客户端。

如果你部署过哨兵集群的话就会知道,在配置哨兵的信息时,我们只需要用到下面的这个配置项,设置主的IP和端口,并没有配置其他哨兵的连接信息。

sentinel monitor <master-name> <ip> <redis-port> <quorum>

哨兵既然不知道彼此的地址,怎么组成哨兵集群呢?

1.基于pub/sub机制的哨兵集群的组成

哨兵能被互相发现的原因:Redis提供的pub/sub机制(发布/订阅机制)

1.1哨兵之间互相被发现过程:

哨兵只要跟主库建立起了连接,就可以在主库上发布信息,发布自己的连接信息(ip和端口号)。同时也可以从主库上订阅信息,获取其它哨兵发布的连接信息。当多个哨兵在主库上进行了发布和订阅后,他们就互相知道彼此的ip地址和端口号。

除了哨兵实例,我们自己编写的应用程序也可以通过Redis进行消息发布和订阅。

Redis如何区分不同的应用程序?

Redis通过频道的方式。对消息进行归类管理,区分不同的应用消息。频道其实就是消息类型,当消息类型相同时,就属于一个频道。只有订阅了同一个频道的应用才能通过发布的消息进行信息交换。

在主从集群中,主库存在“__sentinel__:hello”的频道,不同哨兵通过实现他来相互发现,实现互相通信。

1.2哨兵消息互通说明:

例如:在下图中,哨兵1把自己的IP (17216.19.3) 和端口26579) 发布到“_sentinel_:hello”频道上,哨兵2和3订阅了该频道。那么此时,哨兵2和3就可以从这个频道直接获取哨兵1的IP地址和端口号。

然后,哨兵2、3可以和哨兵1建立网络连接。通过这个方式,哨兵2和3也可以建立网络连接,这样一来,哨兵集群就形成了。它们相互间可以通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商

哨兵除了需要彼此建立连接形成集群外还需要和从库建立连接。因为在哨兵监控任务中,哨兵需要对主从库进行心跳判断,而且在主从库切换完成后,还需要通知从库,让他们和新主库进行同步。

哨兵如何跟从库简历连接?如何知道从库的ip地址和端口呢?

1.3哨兵与从库建立连接:INFO命令

当哨兵给主库发送INFO命令时,主库接收命令后,会将从库列表返回给哨兵。哨兵接收从库列表连接信息后,会和每个从库建立连接,并且在这个连接上持续的对从库进行监控。

通过pub/sub机制,哨兵之间建立哨兵集群,又通过发送INFO命令,获取从库连接信息 ,哨兵与从库建立连接,进行监控。主从库切换后,客户端需要知道新主库的连接信息,才能像新主库发送信息。因此哨兵还需要完成将新主库信息告诉客户端的任务。

在实际使用哨兵时,我们有时会遇到这样的问题:如何在客户端通过监控了解哨兵进行主从切换的过程呢?比如说,主从切换进行到哪一步了? 这其实就是要求,客户端能够获取到哨兵集群在监控、选主、切换这个过程中发生的各种事件.

1.4哨兵与客户端间的信息同步

1.5基于pub/sub机制的客户端事件的通知

从本质上说,哨兵就是一个运行在特定模式下的Redis实例,只不过它并不完成服务请求操作,只是完成监控、选主和通知的任务。所以,每个哨兵实例也提供pub/sub机制,客户端可以从哨兵订阅消息。哨兵提供的消息订阅频道有很多,不同频道包含了主从库切换过程中的不同关键事件

常用事件:

事件

相关频道

主库下线事件

+sdown(实例进入“主观下线”状态)

-sdown(实例退出“主观下线”状态)

+odown(实例进入“客观下线”状态)

-odown(实例退出“客观下线”状态)

从库重新配置事件

+slave-reconf-sent(哨兵发送SLACEOF命令重新配置从库)

+slave-reconf-inprog(从库配置新主库,但尚未进行同步)

+slave-reconf-done(从库配置新主库,且与新主库同步完成)

新主库切换

+switch-master(主库地址变化)

知道了这些频道之后,就可以让客户端从哨兵这里订阅消息了。具体的操作步骤是,客户端读取哨兵的配置文件后,可以获得哨兵的地址和端口,和哨兵建立网络连接。然后,可以在客户端执行订阅命令,来获取不同的事件消息。

举个例子,你可以执行如下命令,来订阅“所有实例进入客观下线状态的事件”:

subscribe +odown

订阅所有频道

PSUBSCRIBE  *

当哨兵把新主库选择出来后,客户端就会看到下面的switch-master事件。这个事件表示主库已经切换了,新主库的IP地址和端口信息已经有了。这个时候,客户端就可以用这里面的新主库地址和端口进行通信了。

switch-master <master name> <oldip><oldport> <newport>

通过事件通知,客户端不仅仅可以在主从切换后得到新主库的连接信息,还可以监控得到主从库切换过程中发生的各个重要事件。这样客户端就可以知道主从切换到哪一步了,有助于了解切换速度。

总结:有了pub/sub机制,哨兵可以和从库之间,哨兵与哨兵之间,哨兵与客户端之间建立起连接。主库下线判断,选主依据哨兵集群的监控、选主和通知三个任务基本上可以正常工作了。

主从故障后,集群存在多个实例,怎么确定由哪个哨兵来进行实际的主从切换呢?

2.哪个哨兵执行主从切换?

其实由哪个哨兵进行主从切换的过程,其实跟选主一样也是一个“投票仲裁”的过程。

2.1从哨兵选主,主要过程开始说起:

任何一个实例只要自身判断主库“主观下线”后,就会给其他实例发送is-master-down-by-addr命令。接着,其他实例会根据自己和主库的连接情况,做出Y或N的响应,Y相当于赞成票,N相当于反对票。

一个哨兵获得了仲裁所需的赞成票数后,就可以标记主库为“客观下线”。这个所需的赞成票数是通过哨兵配置文件中的 quorum 配置项设定的。例如,现在有 5 个哨兵,quorum 配置的是 3,那么,一个哨兵需要 3 张赞成票,就可以标记主库为“客观下线”了。这 3 张赞成票包括哨兵自己的一张赞成票和另外两个哨兵的赞成票。

2.2哨兵选出Leader过程:Leader选举

此时,这个哨兵就可以再给其他哨兵发送命令,表明希望由自己来执行主从切换,并让所有其他哨兵进行投票。这个投票过程称为“Leader 选举”。因为最终执行主从切换的哨兵称为 Leader,投票过程就是确定 Leader。

在投票过程中,任何一个想成为 Leader 的哨兵,要满足两个条件:第一,拿到半数以上的赞成票;第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。以 3 个哨兵为例,假设此时的 quorum 设置为 2,那么,任何一个想成为 Leader 的哨兵只要拿到 2 张赞成票,就可以了。

具体通过下图讲解:

在 T1 时刻,S1 判断主库为“客观下线”,它想成为 Leader,就先给自己投一张赞成票,然后分别向 S2 和 S3 发送命令,表示要成为 Leader。

在 T2 时刻,S3 判断主库为“客观下线”,它也想成为 Leader,所以也先给自己投一张赞成票,再分别向 S1 和 S2 发送命令,表示要成为 Leader。

在 T3 时刻,S1 收到了 S3 的 Leader 投票请求。因为 S1 已经给自己投了一票 Y,所以它不能再给其他哨兵投赞成票了,所以 S1 回复 N 表示不同意。同时,S2 收到了 T2 时 S3 发送的 Leader 投票请求。因为 S2 之前没有投过票,它会给第一个向它发送投票请求的哨兵回复 Y,给后续再发送投票请求的哨兵回复 N,所以,在 T3 时,S2 回复 S3,同意 S3 成为 Leader。

在 T4 时刻,S2 才收到 T1 时 S1 发送的投票命令。因为 S2 已经在 T3 时同意了 S3 的投票请求,此时,S2 给 S1 回复 N,表示不同意 S1 成为 Leader。发生这种情况,是因为 S3 和 S2 之间的网络传输正常,而 S1 和 S2 之间的网络传输可能正好拥塞了,导致投票请求传输慢了。

最后,在 T5 时刻,S1 得到的票数是来自它自己的一票 Y 和来自 S2 的一票 N。而 S3 除了自己的赞成票 Y 以外,还收到了来自 S2 的一票 Y。此时,S3 不仅获得了半数以上的 Leader 赞成票,也达到预设的 quorum 值(quorum 为 2),所以它最终成为了 Leader。接着,S3 会开始执行选主操作,而且在选定新主库后,会给其他从库和客户端通知新主库的信息。

如果 S3 没有拿到 2 票 Y,那么这轮投票就不会产生 Leader。哨兵集群会等待一段时间(也就是哨兵故障转移超时时间的 2 倍),再重新选举。这是因为,哨兵集群能够进行成功投票,很大程度上依赖于选举命令的正常网络传播。如果网络压力较大或有短时堵塞,就可能导致没有一个哨兵能拿到半数以上的赞成票。所以,等到网络拥塞好转之后,再进行投票选举,成功的概率就会增加。

需要注意的是,如果哨兵集群只有 2 个实例,此时,一个哨兵要想成为 Leader,必须获得 2 票,而不是 1 票。所以,如果有个哨兵挂掉了,那么,此时的集群是无法进行主从库切换的。因此,通常我们至少会配置 3 个哨兵实例。这一点很重要,你在实际应用时可不能忽略了。

哨兵不会同时投票给自己的原因?

要发生S1、S2和S3同时同自己投票的情况,这需要这三个哨兵基本同时判定了主库客观下线。但是,不同哨兵的网络连接、系统压力不完全一样,接收到下线协商消息的时间也可能不同,所以,它们同时做出主库客观下线判定的概率较小,一般都有个先后关系。文章中的例子,就是S1、S3先判定,S2一直没有判定。

哨兵对主从库进行的在线状态检查等操作,是属于一种时间事件,用一个定时器来完成,一般来说每100ms执行一次这些事件。每个哨兵的定时器执行周期都会加上一个小小的随机时间偏移,目的是让每个哨兵执行上述操作的时间能稍微错开些,也是为了避免它们都同时判定主库下线,同时选举Leader。

Redis 1主4从,5个哨兵,哨兵配置quorum为2,如果3个哨兵故障,当主库宕机时,哨兵能否判断主库“客观下线”?能否自动切换?

1、哨兵集群可以判定主库“主观下线”。由于quorum=2,所以当一个哨兵判断主库“主观下线”后,询问另外一个哨兵后也会得到同样的结果,2个哨兵都判定“主观下线”,达到了quorum的值,因此,哨兵集群可以判定主库为“客观下线”。

2、但哨兵不能完成主从切换。哨兵标记主库“客观下线后”,在选举“哨兵领导者”时,一个哨兵必须拿到超过多数的选票(5/2+1=3票)。但目前只有2个哨兵活着,无论怎么投票,一个哨兵最多只能拿到2票,永远无法达到多数选票的结果。

哨兵实例是不是越多越好?

  • 并不是,我们也看到了,哨兵在判定“主观下线”和选举“哨兵领导者”时,都需要和其他节点进行通信,交换信息,哨兵实例越多,通信的次数也就越多,而且部署多个哨兵时,会分布在不同机器上,节点越多带来的机器故障风险也会越大,这些问题都会影响到哨兵的通信和选举,出问题时也就意味着选举时间会变长,切换主从的时间变久。
  • 哨兵实例越多,误判率会越低,但是在判定主库下线和选举Leader时,实例需要拿到的赞成票数也越多,等待所有哨兵投完票的时间可能也会相应增加,主从库切换的时间也会变长,客户端容易堆积较多的请求操作,可能会导致客户端请求溢出,从而造成请求丢失。如果业务层对Redis的操作有响应时间要求,就可能会因为新主库一直没有选定,新操作无法执行而发生超时报警。
  • 调大down-after-milliseconds后,可能会导致这样的情况:主库实际已经发生故障了,但是哨兵过了很长时间才判断出来,这就会影响到Redis对业务的可用性。

调大down-after-milliseconds值,对减少误判是不是有好处?

是有好处的,适当调大down-after-milliseconds值,当哨兵与主库之间网络存在短时波动时,可以降低误判的概率。但是调大down-after-milliseconds值也意味着主从切换的时间会变长,对业务的影响时间越久,我们需要根据实际场景进行权衡,设置合理的阈值。

3.总结:哨兵关键机制(哨兵联系主从客哨兵,Leader选主)

我们在解决一个系统问题的时候,会引入一个新机制,或者设计一层新功能,哨兵主要内容:为了实现主从切换,我们引入了哨兵;为了避免单个哨兵故障后无法进行主从切换,以及为了减少误判率,又引入了哨兵集群;哨兵集群又需要有一些机制来支撑它的正常运行。

3.1哨兵集群的这些关键机制:

  • 基于 pub/sub 机制的哨兵集群组成过程;
  • 基于 INFO 命令的从库列表,这可以帮助哨兵和从库建立连接;
  • 基于哨兵自身的 pub/sub 功能,这实现了客户端和哨兵之间的事件通知。

对于主从切换,当然不是哪个哨兵想执行就可以执行的,否则就乱套了。所以,这就需要哨兵集群在判断了主库“客观下线”后,经过投票仲裁,选举一个 Leader 出来,由它负责实际的主从切换,即由它来完成新主库的选择以及通知从库与客户端。

最后,我想再给你分享一个经验:要保证所有哨兵实例的配置是一致的,尤其是主观下线的判断值 down-after-milliseconds。这个值在不同的哨兵实例上配置不一致,导致哨兵集群一直没有对有故障的主库形成共识,也就没有及时切换主库,最终的结果就是集群服务不稳定。所以,你一定不要忽略这条看似简单的经验。

三.哨兵全部总结

1.哨兵怎么跟主库连接

哨兵与主库直接相关联,手动设置,可以设置多个哨兵

2.哨兵怎么给从库发消息

哨兵给主库发送info命令,主库返回从库的slave集合,与从库建立连接,把新主库的信息发送给从库

3.哨兵怎么跟客户端联系

client订阅Sentinel的某一个频道,该频道里有谁是master的信息,读取哨兵的配置文件,获取ip地址和端口号,与哨兵建立连接,连接之后订阅信息,获取主库信息,并与主库建立连接

猜你喜欢

转载自blog.csdn.net/qq_45656077/article/details/129749356