Redis高可用之哨兵机制实现细节

news2025/2/2 16:46:20

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 命令实时更新节点拓扑信息。

执行info命令

有了集群的信息,哨兵终于可以开始它的工作了。第一项职责:判断主从库是否下线。

三、如何判断主从库下线?

3.1 定时执行 ping 命令

哨兵进程在运行时,每隔1秒,会向主节点、 从节点、 其余 Sentinel 节点发送一条 ping 命令,检测它们是否仍然在线运行。如果主、从库没有在规定时间内响应哨兵的 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/redis/blob/unstable/sentinel.conf
  • [3] https://redis.io/docs/management/sentinel/#pubsub-messages

附 Redis 文档

  • http://www.redis.cn/topics/sentinel.html
  • https://redis.io/docs/management/sentinel

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/104001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【树莓派不吃灰】兄弟连篇⑥ Linux系统进程管理

目录1、进程查看1.1 ps1.2 top1.3 pstree2、终止进程2.1 kill2.2 killall2.3 pkill3、工作管理4、系统资源查看4.1 vmstat 监控系统资源4.2 dmesg 开机内核检测信息4.3 free 查看内存使用4.4 查看cpu信息4.5 uptime4.6 uname4.7 判断当前系统位数4.8 查询当前linux发行版本4.9 …

Windows及Kail安装配置

apache在kali环境搭建 Kali虚拟机中是包含有Apache的&#xff0c;在/etc目录下ls即可显示出来&#xff0c; 所以这里只需要进行配置就可以了。 图1.1 Apache2目录 打开Apache服务&#xff0c;开启后可以使用status命令查看服务状态。 /etc/init.d/apache2 start /etc/init.d…

【5】控制语句

指针 Go中不用“->”运算符&#xff0c;用的是 “.” 选择符“&”&#xff1a;取地址符“*”&#xff1a;访问目标对象符默认值为&#xff1a;nil (不是NULL)、- -:作为语句&#xff0c;只可以自己放一行&#xff0c;而且放在右边&#xff08;不是表达式&#xff09; …

一款强大的API接口文档管理工具(Smart-Doc + Torna)

【本文由龙飞同学供稿】 在团队协作开发项目的时候&#xff0c;接口文档承担着向其他开发人员说明接口相关信息的重要任务&#xff0c;因此&#xff0c;一份清晰而又相近的接口文档至关重要。 但是&#xff0c;写接口文档的痛苦想必各位开发人员都体验过&#xff0c;明明写接…

在职读研有意义么?来社科院与杜兰大学金融管理硕士项目探寻答案

在职场当我们遇到变化&#xff0c;尤其是发展上有瓶颈期的时候&#xff0c;我们会寻找向内突破&#xff0c;通过提升自己的核心竞争力来应对&#xff0c;通过再学习来增加自身的优势。那么在职读研有意义吗&#xff1f;我们来社科院与杜兰大学金融管理硕士项目来探寻。 一、读在…

Postman接口测试之Mock快速入门

一、Mock简介 1.Mock定义 Mock是一种比较特殊的测试技巧&#xff0c;可以在没有依赖项的情况下进行接口或单元测试。通常情况下&#xff0c;Mock与其他方法的区别是&#xff0c;用于模拟代码依赖对象&#xff0c;并允许设置对应的期望值。简单一点来讲&#xff0c;就是Mock创建…

【专业数据】八.2020~2022年北京交通大学【人工智能】专业复试线/分数线差/计划招生数/复试数/录取数/复试比例/录取率

文章目录 1.专业介绍2.2020-2022年国家线/复试线/分数线差2.1.数据总览2.2.数据指标2.2.1.复试分数线2.2.2.分数线差3.2020-2022年计划招生数/复试数/录取数/复试比例/录取率3.1.数据总览3.2.数据指标3.2.1.复试比例3.2.2.录取率4.参考资料欢迎订阅本专栏:《北交计算机复试经验…

python+appium(4)

conftest进阶使用 思路&#xff1a;我们平时导包多&#xff0c;而且经常使用某一段代码&#xff0c;一般我们使用封装的思想进行优化&#xff0c;pytest中conftest模块提供了更高效的方法 你可以添加任何想一次性封装的代码&#xff0c;这里吧代码变成变量&#xff0c;需要使…

Android APP 自动化测试搭建:Appium + Genymotion + [Robot Framework] Open Application

Android APP 自动化测试搭建&#xff1a; Appium Genymotion [Robot Framework] Open Application1. 安装 Android SDK2. 安装 ADB&#xff08;Android Debug Bridge&#xff09;3. 配置 platform-tools 目录(1) 复制 adb 等工具(2) 添加 apksigner.jar4. 配置 Appium Server…

C语言—函数

函数&#xff1a;将代码块封装成一个个不同的函数&#xff0c;在使用时可以多次的调用&#xff0c;不需要关心内部的实现&#xff1b; #include <stdio.h>void MyPrint() {printf("&&&&&&&&&&&\n");printf("…

BaseDet: 走过开发的弯路

BaseDet 开源啦&#xff01;该 repo 提供了一些经典的检测 SOTA 模型以及相关组件&#xff0c;欢迎大家按需取用~~GitHub&#xff1a;https://github.com/megvii-research/basedetMegStudio 使用示例&#xff1a;https://studio.brainpp.com/project/28826?nameBaseDet%E4%BD%…

重磅!TDengine 3.2.0 正式发布

近日&#xff0c;TDengine 3.0.2.0 正式发布了。这是自今年 8 月份 TDengine 3.0 发布以来的第一个重要改进版本。 TDengine 3.0 带来了几大核心特性&#xff0c;包括云原生架构、流式计算&#xff0c;还增强了数据订阅功能&#xff1b;更重要的是&#xff0c;3.0 系列版本开始…

Python是个什么鬼?为什么那么多人都要学它?真的有这么牛吗?

为什么那么多人选择学习python&#xff1f; Python在人工智能、大数据、自动化运维、全栈开发等方面具有独特的优势。随着Python继续占据编程语言主流的趋势&#xff0c;全国各城市的招聘岗位和薪酬将大幅增加。此外&#xff0c;随着人工智能在中国的投资和规划&#xff0c;对…

C++ Primer 第三章 Strings, Vectors, and Arrays

C Primer 第三章 Strings, Vectors, and Arrays3.1. Namespace using Declarations3.2. Library string Type3.2.1. Defining and Initializing stringsDirect and Copy Forms of Initialization3.2.2. Operations on stringsReading and Writing stringsUsing getline to Read…

【图像处理】opencv | 形态学运算:腐蚀,膨胀,开运算,闭运算| 二值图像处理

文章目录前言一、腐蚀和膨胀1.1腐蚀1.2膨胀二、开运算与闭运算三、礼帽与黑帽前言 参考视频&#xff1a;opencv教学 参考教材&#xff1a;《数字图像处理基础》 我的代码基本是跟着B站的视频里面敲了一遍&#xff0c;然后结合教材对指定区域做了一些加强学习 一、腐蚀和膨胀 …

华为云APIArts:API全生命周期一体化解决方案,帮助您端到端呵护您的API

摘要&#xff1a;华为云API Arts是API设计、API开发、API测试、API托管、API运维、API变现一体化协作平台&#xff0c;通过维护API各开发阶段数据高度一致&#xff0c;支持开发者高效实现API全流程一站式体验。 伴随数字化浪潮的到来&#xff0c;应用编程接口(API)已经成为一个…

【MySQL】2.MySQL库操作

文章目录1.0 MySQL基本使用1.1 理解数据库操作2.0 MySQL数据库操作详解2.1创建数据库2.2 字符集和校验规则2.2修改数据库2.3删除数据库2.4查看数据库链接1.0 MySQL基本使用 1.1 理解数据库操作 查看数据库配置文件 指令: vim /etc/my.cnf 登录数据库 指令&#xff1a; mysql…

[第十三届蓝桥杯/java/算法]A——排列字母

&#x1f9d1;‍&#x1f393;个人介绍&#xff1a;大二软件生&#xff0c;现学JAVA、Linux、MySQL、算法 &#x1f4bb;博客主页&#xff1a;渡过晚枫渡过晚枫 &#x1f453;系列专栏&#xff1a;[编程神域 C语言]&#xff0c;[java/初学者]&#xff0c;[蓝桥杯] &#x1f4d6…

机器人开发--设计范式

机器人开发--设计范式1 概念范式特点2 三种范式2.1 机器人基元&#xff1a;感知&#xff08;sense&#xff09;、规划&#xff08;plan&#xff09;、执行&#xff08;act&#xff09;2.2 范式分类分级范式 hierarchical paradigm反应范式 reactive paradigm混合范式 hybrid pa…

程序的动态链接(5):使用动态库

前言 Linux下动态库文件的命名规范是以lib开头&#xff0c;紧接着是动态库名&#xff0c;以.so为后缀名&#xff0c;即lib 动态库名.so。 动态库查找过程 在Linux下&#xff0c;动态库的搜索的优先级顺序为&#xff1a; 编译目标代码时指定的动态库搜索路径&#xff0c;保…