共计 5233 个字符,预计需要花费 14 分钟才能阅读完成。
Redis 高可用之哨兵机制实现细节
本文来自我的 technotes [1] Redis 篇,欢送你常来逛逛。
注释
在上一篇的文章《Redis 高可用全景一览》中,咱们学习了 Redis 的高可用性。高可用性有两方面含意:一是服务少中断,二是数据少失落。主从库模式和哨兵保障了服务少中断,AOF 日志和 RDB 快照保障了数据少失落。
并且咱们学习了哨兵三个职责,别离是:监控、选主(抉择主库)和告诉。明天咱们就来具体学习一下。
首先呐,在哨兵启动前,咱们要对哨兵进行配置。Redis 源码中蕴含了一个名为 sentinel.conf [2] 的文件,该文件中局部配置如下:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
第一行配置批示 Sentinel 去监督一个名为 mymaster 的主服务器,这个主服务器的 IP 地址为 127.0.0.1,端口号为 6379,而将这个主服务器判断为生效至多须要 2 个 Sentinel 批准。
能够看到,咱们仅仅设置了主库的 IP 和端口。并没有配置其余哨兵的连贯信息啊,这些哨兵实例既然都不晓得彼此的地址,又是怎么组成集群的呢?
哨兵实例之间能够互相发现,要归功于 Redis 提供的 pub/sub 机制,也就是公布 / 订阅机制。在主从集群中,主库上有一个名为 __sentinel__:hello
的频道,不同哨兵就是通过它来互相发现,实现相互通信的。
一、哨兵集群的组成
每隔 2 秒,每个 Sentinel 节点就会向 Redis 数据节点的 __sentinel__:hello
频道上发送该 Sentinel 节点对于主节点的判断以及以后 Sentinel 节点的信息。
举个例子。在上图中,哨兵 1 把本人的 IP(172.16.19.3)和端口(26579)公布到 __sentinel__:hello
频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就能够从这个频道间接获取哨兵 1 的 IP 地址和端口号。
而后,哨兵 2、3 能够和哨兵 1 建设网络连接。通过这个形式,哨兵 2 和 3 也能够建设网络连接,这样一来,哨兵集群就造成了。它们相互间能够通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商。
哨兵除了彼此之间建设起连贯造成集群外,还须要和从库建设连贯。这是因为,在哨兵的监控工作中,它须要对主从库都进行心跳判断,而且在主从库切换实现后,它还须要告诉从库,让它们和新主库进行同步。
那么,哨兵又是如何晓得从库的 IP 地址和端口的呢?
二、获取从节点信息
这是由哨兵向主库发送 INFO 命令来实现的。就像下图所示,哨兵 2 给主库发送 INFO 命令,主库承受到这个命令后,就会把从库列表返回给哨兵。接着,哨兵就能够依据从库列表中的连贯信息,和每个从库建设连贯。哨兵 1 和 3 也能够通过雷同的办法和从库建设连贯。
上面是在一个主节点上执行 info 命令的后果片段:
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=4917,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=4917,lag=1
之后,哨兵会在这个连贯上继续地对从库进行监控。每隔 10 秒,哨兵节点就会向主节点和从节点发送 info 命令,获取集群最新的拓扑构造。这样,当有新的从节点退出时就能够立即感知进去。节点不可达或者选定新主库后,也能够通过 info 命令实时更新节点拓扑信息。
有了集群的信息,哨兵终于能够开始它的工作了。第一项职责:判断主从库是否下线。
三、如何判断主从库下线?
3.1 定时执行 ping 命令
哨兵过程在运行时,每隔 1 秒,会向主节点、从节点、其余 Sentinel 节点发送一条 ping 命令,检测它们是否依然在线运行。如果主、从库没有在规定工夫内响应哨兵的 ping 命令,哨兵就会把它标记为「下线状态」。
如果检测的是主库,那么,哨兵还不能简略地开启主从切换。因为很有可能存在这么一个状况:那就是哨兵误判了,其实主库并没有故障。
误判个别会产生在集群网络压力较大、网络拥塞,或者是主库自身压力较大的状况下。一旦哨兵误判,启动了主从切换,后续的选主和告诉操作都会带来额定的计算和通信开销。
那怎么缩小误判呢?
3.2 主观下线和主观下线
哨兵机制通常会采纳多实例组成的集群模式进行部署,这也被称为哨兵集群。引入多个哨兵实例一起来判断,就能够防止单个哨兵因为本身网络情况不好,而误判主库下线的状况。同时,多个哨兵的网络同时不稳固的概率较小,由它们一起做决策,误判率也能升高。
还记得我在文章结尾给出的 sentinel.conf 配置吗?
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
down-after-milliseconds
选项就是 Sentinel 认为服务器曾经断线的临界阈值。
如果服务器在该毫秒数之内,没有返回 Sentinel 发送的 ping 命令的回复,或者返回一个谬误,那么 Sentinel 将这个服务器标记为主观下线(subjectively down,简称 SDOWN)。
如果没有足够数量的 Sentinel 批准主库曾经下线,当主库从新向 Sentinel 的 PING 命令返回无效回复时,主库的主观下线状态就会被移除。而如果超出 2 个 Sentinel 都将主库标记为主观下线之后,主库才会被标记为主观下线(objectively down,简称 ODOWN)。哨兵就要开始下一个决策过程了,即从许多从库中,选出一个从库来做新主库。
四、如何选定新主库?
4.1 初步筛选
构想一下,如果在选主时,一个从库失常运行,咱们把它选为新主库开始应用了。可是,很快它的网络出了故障,此时,咱们就又得从新选主了。这显然不是咱们冀望的后果。所以,在选主时,除了要查看从库的以后在线状态,还要判断它之前的网络连接状态。如果从库总是和主库断连,而且断连次数超出了肯定的阈值,咱们就有理由置信,这个从库的网络情况并不是太好,就能够把这个从库筛掉了。
具体怎么判断呢?你应用配置项 down-after-milliseconds * 10。其中,down-after-milliseconds 是咱们认定主从库断连的最大连贯超时工夫。如果在 down-after-milliseconds 毫秒内,主从节点都没有通过网络分割上,咱们就能够认为主从节点断连了。如果产生断连的次数超过了 10 次,就阐明这个从库的网络情况不好,不适宜作为新主库。
这样咱们就过滤掉了不适宜做主库的从库,实现了筛选工作。
接下来就要给残余的从库打分了。咱们能够别离依照三个规定顺次进行三轮打分,这三个规定别离是从库优先级、从库复制进度以及从库 ID 号。
4.2 三轮打分
第一轮:优先级最高的从库得分高。
用户能够通过 slave-priority 配置项,给不同的从库设置不同优先级。比方,你有两个从库,它们的内存大小不一样,你能够手动给内存大的实例设置一个高优先级。
第二轮:和旧主库同步水平最靠近的从库得分高。
这个规定的根据是,如果抉择和旧主库同步最靠近的那个从库作为主库,那么,这个新主库上就有最新的数据。
如何判断从库和旧主库间的同步进度呢?
主从库同步时有个命令流传的过程。在这个过程中,主库会用 master_repl_offset 记录以后的最新写操作在 repl_backlog_buffer 中的地位,而从库会用 slave_repl_offset 这个值记录以后的复制进度。
主从库同步时有个命令流传的过程。在这个过程中,主库会用 master_repl_offset 记录以后的最新写操作在 repl_backlog_buffer 中的地位,而从库会用 slave_repl_offset 这个值记录以后的复制进度。
如下图所示,从库 2 就应该被选为新主库。
第三轮:ID 号小的从库得分高。
每个实例都会有一个 ID,这个 ID 就相似于这里的从库的编号。目前,Redis 在选主库时,有一个默认的规定:在优先级和复制进度都雷同的状况下,ID 号最小的从库得分最高,会被选为新主库。
到这里,新主库就被选出来了,接下来就是将从库降级为主库。然而问题又来了,这么多哨兵,该由谁来执行主从切换操作呢?
4.3 由哪个哨兵执行主从切换?
任何一个哨兵实例只有本身判断主库“主观下线”后,就会向其余 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为主库已下线。接着,其余哨兵实例会依据本人和主库的连贯状况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。
此时,这个哨兵就能够再给其余哨兵发送命令,表明心愿由本人来执行主从切换,并让所有其余哨兵进行投票。这个投票过程称为“Leader 选举”。选举进去的 Leader 就是最终执行主从切换的哨兵。
例如,当初有 3 个哨兵,quorum 配置的是 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 会开始执行选主操作,而且在选定新主库后,会给其余从库和客户端告诉新主库的信息。
五、将新主库告诉给从库和客户端
通过上文的学习,咱们晓得哨兵能够向主库发送 INFO 命令,来获取从库的 IP 地址和端口。
然而,哨兵不能只和主、从库连贯。因为,主从库切换后,客户端也须要晓得新主库的连贯信息,能力向新主库发送申请操作。所以,哨兵还须要把新主库的信息通知客户端。
那怎么把新主库的信息通知客户端呢?
5.1 基于 pub/sub 机制的客户端事件告诉
从实质上说,哨兵就是一个运行在特定模式下的 Redis 实例,只不过它并不服务申请操作,只是实现监控、选主和告诉的工作。所以,每个哨兵实例也提供 pub/sub 机制,客户端能够从哨兵订阅音讯。
下图示中是一些重要的频道,以及波及的几个要害事件。更多的频道你能够在文末链接 [3] 中查看。
客户端从主库读取哨兵的配置文件后,能够取得哨兵的地址和端口,和哨兵建设网络连接。
当哨兵把新主库抉择进去后,客户端就会看到上面的 switch-master 事件。这个事件示意主库曾经切换了,新主库的 IP 地址和端口信息曾经有了。这个时候,客户端就能够用这外面的新主库地址和端口进行通信了。
switch-master <master name> <oldip> <oldport> <newip> <newport>
小结
至此,哨兵的工作职责及细节咱们就学习完了。我整顿了本文常识消化链路,如下。
在 sentinel.conf 中配置哨兵
-> 没有配置其余哨兵 ip,怎么组成集群的?-> 哨兵是怎么晓得从库的 IP 和端口的?-> 职责 1:如何判断主从库下线了?-> 职责 2:如何选定新主库?-> 由哪个哨兵执行主从切换?-> 职责 3:如何把新主库通知客户端?
更多技术干货,我会在前面的内容里持续分享,敬请关注。公众号「杨同学 technotes」,欢送技术交换。
文中所提及的链接
- [1] https://www.dbses.cn/technotes
- [2] https://github.com/redis/redi…
- [3] https://redis.io/docs/managem…
附 Redis 文档
- http://www.redis.cn/topics/se…
- https://redis.io/docs/managem…