Redis原理之哨兵

Redis原理之哨兵

主从库集群模式下,如果从库发生故障,客户端可以继续向主库或其他从库发起请求,进行相关操作。但是如果主库发生故障,就会直接影响从库的同步,和写操作。因此需要能够有一种机制,实现主从库自动切换,就是哨兵机制。

1. 哨兵机制的基本流程

哨兵就是一个运行在特殊模式下的Redis进程,主从库实例运行的同时,它也在运行。哨兵主要负责就是三个任务:监控、选主(选择主库)和通知

  1. 监控:是指哨兵进行在运行时,周期性的给所有的主从库发送PING命令,检测它们是否仍然在线运行。如果从库没有在规定时间内响应哨兵的PING命令,哨兵就会把它标记为下线状态;同样,如果主库没有响应PING命令,也会被判定主库下线。

  2. 选主:主库挂了之后,哨兵需要从多个从库中,按照一定的规则选择一个从库实例,执行slaveof no one命令,把它作为新的主库。

  3. 通知:哨兵会把新主库的连接信息发送给其他从库,让它们执行slaveof(或replicaof)命令,和新主库建立连接,并进行数据复制。同时,哨兵会把新主库的连接信息通知给客户端,让它们把请求发送到新主库上。并且会将原来的主库,更新为从库,并保持对其监控;当旧的主库恢复后,哨兵会命令它去复制新的主库。

哨兵机制三项任务与目标 ,如下图所示:

2. 监控

哨兵通过三个定时监控任务,来完成对各个节点的发现和监控。

1)每隔10秒 ,每个哨兵会向主库和从库 发送INFO命令,获取最新的拓扑结果。哨兵对命令结果进行解析就可以找到相应的从库。这个定时任务作用具体有以下几点:

  • 通过向主库执行info命令,获取从库的信息,这也是为什么不需要配置从库信息。
  • 当有新的从库加入时,可以立刻感知出。
  • 节点不可达或故障转移后,可以通过Info命令实时更新节点拓扑信息。

2)每隔2秒 ,每个哨兵节点会向主库、从库和其他哨兵__sentinel__:hello频道上,发送该哨兵节点中主库的信息,以及当前哨兵节点的信息。同时每个哨兵节点也会订阅该频道(订阅每一个主库、从库,通过重写publishCommand命令),来了解其他哨兵节点以及它们对主库的判断。这个定时任务可以完成两个工作:(命令格式:<sentinel_ip>,<sentinel_port>,<sentinel_runid>,<sentinel_epoch>,<master_name>,<master_ip>,<master_port>,<master_epoch>)

  • 发现新的哨兵节点,通过订阅主库的__sentinel__:hello了解其他哨兵节点的信息。
  • 哨兵节点了解其他哨兵对主库的判断。如果出现了主库切换,那么可以从这里得知。

3)每隔1秒 ,每个哨兵节点会向主库、从库和其他哨兵发送一条ping命令,做一次心跳检测,来确认这些节点当前是否可达。

每个哨兵会对主库、从库、其他哨兵创建连接。其中会对主库和从库创建命令连接和消息连接对其他哨兵只会创建命令连接 。命令连接用于发送命令并接收命令回复,消息连接用于订阅__sentinel__:hello 频道。

连接都是长连接。只是哨兵特地说明下,需要和其他哨兵建立连接。至于为什么要建立两条连接,主要做通道隔离,详细可以看《redis设计与实现》,16.1.5中的说明

3. 选主

3.1 主观下线和客观下线

哨兵进程会使用PING命令检测它自己和主、从库的网络情况,用来判断实例的状态。如果发现主、从库没有响应PING命令(超过配置项:down-after-millseconds没有回复,默认值30秒),则哨兵就会先把它标记为主观下线。

如果检测的是从库,那么只要简单标记为主观下线即可,因为从库的下线影响一般不大。但是如果检测的是主库,除了标记为主观下线外,还不能开启主从切换,因为可能存在误判:主库实际并没有下线,但是哨兵误以为它下线了。在集群网络压力较大、网络拥塞、或者主库压力大的时候有可能出现误判。

为了减少误判,通常哨兵会采用多实例组成的集群模式进行部署,也称为哨兵集群。引入多个哨兵实例一起判断,就可以避免单个哨兵因为自身网络状况不好,而误判主库下线的情况。在大多数的哨兵实例,都判断主库已经主观下线了,那么主库就会被标记为客观下线。

判断的原则是:少数服从多数。当有N个哨兵实例时,最好要有个(可以配置)实例判断主库为主观下线。

具体为:当哨兵主观下线的节点是主库时,该哨兵会通过is-master-down-by-addr命令,向其他哨兵询问对主库的状态,当超过(根据配置)个数的哨兵认为主库确实有问题,该哨兵节点会做出客观下线的决定。

例如:默认quorum配置为2,此时有3个哨兵,那么一个哨兵需要2个哨兵认为"主观下线",就可以标记主库为"客观下线"了,这个两个哨兵包括自己,和另外一个哨兵。

3.2 哪个哨兵执行主从切换?

当哨兵发现主库为客观下线时,多个哨兵之间会选举出一个Leader。确定哪个哨兵执行主从切换的过程,和主库"客观下线"的判断过程一样,是一个"投票仲裁"的过程。当发现主库客观下线时,这个哨兵就可以给其他哨兵发送is-master-down-by-addr命令(数据不一样,最后一个runId为哨兵的runId),表明希望由自己来执行主从切换,并让所有其他哨兵进行投票。这个投票过程称为"Leader选举"。因为最终执行主从切换的哨兵称为Leader,投票过程就是确定Leader。

在投票过程,任何一个想成为Leader的哨兵,需要满足两个条件:

  1. 拿到半数以上的赞成票。(N/2 + 1)

  2. 拿到的票数同时,还需要大于等于哨兵配置文件中quorum的值。默认quorum为2,3个哨兵实例,那么任何一个想成为Leader的哨兵只要拿到2张赞成票就可以了。

另外,如果在一轮投票中没有产生Leader,哨兵集群会等待一段时间(默认下为10s。min(10s,failover-timeout)),再重新选举,这是因为,哨兵集群能够进行成功投票,很大程度依赖于选举命令能正常网络传播。如果网络压力比较大,或有阻塞,就可能导致没有一个哨兵能拿到半数以上的赞成票。所以等到网络拥塞好转后,在进行投票选举,成功的概率可就会增加。

还有一点需要注意,如果哨兵集群只有2个实例,此时,一个哨兵要想成为Leader,必须获得2票,而不是1票。所以如果有个哨兵挂掉了,此时集群无法进行主从库切换,因此,至少会配置3个哨兵实例

3.3 如何选新的主库?

可以把哨兵选择新主库的过程,称为:筛选 + 打分。从多个从库中,先按照一定的筛选条件,把不符合条件的从库过滤掉,然后再按照一定的规则,给剩下的从库打分,将得分最高的从库选为新主库。

筛选条件:首先肯定要保证从库仍然在线运行,还要判断它之前的网络连接状态(防止成为新主库后,网络出现故障,又要重新选主)。如果从库和主库断连时间过长,就可以认为这个从库的网络状况不太好,可以过滤掉。

打分:按照三个规则一次进行三轮打分。分别是从库优先级、从库复制进度以及从库ID号。主要在某一轮中,有从库得分最高,那么它就是新主库,选主过程结束。如果没有出现得分最高的从库,那么就继续进行下一轮。

第一轮:优先级最高的从库得分高。

用户可以通过slave-priority配置项,给不同的从库设置不同优先级。在选主时,哨兵会给优先级最高的从库打高分,如果有一个从库优先级最高,那么它就是新从库了。如果从库优先级一样,那么开始第二轮打分。

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

如果选择和旧主库同步最接近的那个从库作为主库,那么新主库上就有最新的数据。那么如何判断从库和旧主库的同步进度呢?在主从同步时有提到,主库会用master_repl_offset记录当前最新写操作在repl_backlog_buffer中的位置,而从库会用slave_repl_offset这个值记录当前的复制进度。所以,要找的从库,它的slave_repl_offset需要最接近master_repl_offset(实际代码中是在从库中找slave_repl_offset最大的)

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

每个实例都会有一个ID,这个ID就类似于这里的从库编号。目前Redis在选主库时,有一个默认规定:在优先级和复制进度都相同的情况下,ID号最小的从库得分最高,会被选为新主库。

4. 通知

在配置哨兵信息时,只需要用到下面配置项,设置主库的IP和端口,并没有配置其他哨兵的连接信息。哨兵一般也是集群部署,内部会有机制能够相互发现。

xml 复制代码
sentinel monitor <master-name> <ip> <redis-port> <quorum>

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

哨兵之间可以相互发现,要归功于Redis提供的pub/sub机制。哨兵只要和主库建立连接,就可以在主库上发布消息。同时也可以在主库上订阅消息,获取其他哨兵发布的连接信息。当多个哨兵实例都在主库上做了发布和订阅操作后,它们之间就能知道彼此的IP地址和端口。

哨兵通过_sentinel_:hello频道,来相互发现和通信。

哨兵向主库发送INFO命令来获取从库的IP地址和端口

通过pub/sub机制,哨兵之间可以组成集群,同时哨兵又通过INFO命令,获得从库连接信息,也可以和从库建立连接,进行监控。但哨兵不能只和主从库连接,因为主从库切换后,客户端也需要知道新主库的连接信息,才能向新主库发送请求。所以哨兵还需要把新主库的信息告诉客户端。

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

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

客户端读取哨兵的配置文件后,可以获得哨兵的地址和端口,和哨兵建立网络连接。然后可以在客户端执行订阅命令,来获取不同的事件消息。有了这些事件,客户端不仅可以知道主从切换后新主库的连接信息,还可以知道主从切换到了哪一步,有助于了解切换进度。

4.3 Jedis SentinelPool库如何实现主从切换

1)先通过哨兵的sentinel get-master-addr-by-name 命令获取主库的地址。

2)为每个哨兵节点开启主库切换的监控线程。内部会订阅sentinel的+switch-master 频道,当出现主从切换的时候,就知道新的主库了。

2)会为每个sentinel节点开启主节点切换的监控线程。内部会订阅sentinel的+switch-master 频道,当出现主从切换的时候,就知道新的Master了。

5. Sentinel配置

5.1 monitor

xml 复制代码
sentinel monitor <master-name> <ip> <port> <quorum>

哨兵要监控主库,所以配置上必然要知道监控哪个节点。该配置表示要监控一个名字叫,ip地址和端口号为的主库。代表判定主库最终不可达所需要的票数。哨兵能够从主库中,获取从库和其余哨兵节点的信息。

用于故障发现和判定,如将quorum配置为2,说明至少要有2个哨兵节点认为主库不可达,才会判定为客观下线。

同时还与哨兵节点的领导者选举有关,至少要有max(quorum,(num(sentinels)/2 + 1)) 个哨兵节点参与选举,才能选主领导者哨兵。

比如有5个哨兵,quorum为2,哨兵挂了3个。这个时候主库挂掉,虽然可以判定为客观下线,但是哨兵已经没有办法选出一个Leader进行故障转移。

5.2 down-after-milliseconds

xml 复制代码
sentinel down-after-milliseconds <master-name> <times> 默认30s

每个哨兵节点都要通过定期发送ping命令来判断Redis数据节点与其余哨兵节点是否可达,如果超过down-after-milliseconds配置的时间没有回复,则判断节点不可达。down-after-milliseconds虽然以为参数,但实际上对哨兵节点、主库、从库的失败判定同时有效。

down-after-milliseconds值越大,代表哨兵节点对节点不可达条件判断越宽松,条件宽松会导致应用方需要等待故障转移的时间越长、条件严格虽然可以及时发现并完成故障转移,但也存在一定的误判率。

5.3 failover-timeout

xml 复制代码
sentinel failover-timeout <master-name> <times> 默认180秒

failover-timeout通常被解释成故障转移超时时间,当实际上它作用故障转移的各个阶段。

a)选出合适从库。

b)晋升选出的从库为主库。

c)命令其余从库复制新的主库。

d)等待原主库恢复后,命令它去复制新的主库。

failover-timeout作用体现以下四方面:

1)如果哨兵对一个主库故障转移失败,那么下次再对该主库做故障转移的起始时间是failover-timeout的2倍。

2)在b)阶段,如果哨兵节点向a)阶段选出的从库执行 slaveof no one 一直失败(如该从库此刻出现故障),当此过程超过failover-timeout时,则故障转移失败。

3)在b)阶段如果执行成功,哨兵节点还会执行info命令来确认a)阶段选出来的从库确实晋升为主库,如果此过程执行时间超过failover-timeout时,则故障转移失败。

4)如果c)阶段执行时间超过failover-timeout(不包含复制时间),则故障转移失败。注意即使超过这个时间,哨兵节点也会最终配置从库去同步最新的主库。

6. 参考资料

1)《Redis核心技术与实战》------极客时间

2)《Redis开发与运维》

3)《Redis设计与实现》

相关推荐
2401_857622661 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589361 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没2 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch3 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
杨哥带你写代码4 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
AskHarries5 小时前
读《show your work》的一点感悟
后端
A尘埃5 小时前
SpringBoot的数据访问
java·spring boot·后端
yang-23075 小时前
端口冲突的解决方案以及SpringBoot自动检测可用端口demo
java·spring boot·后端
Marst Code5 小时前
(Django)初步使用
后端·python·django
代码之光_19805 小时前
SpringBoot校园资料分享平台:设计与实现
java·spring boot·后端