上一篇文章介绍了高可靠方案:主从集群模式。通过主从库的读写分离,来保证服务的可靠性。

当某个从库出现故障时,不影响服务的使用,主库仍然可以处理写命令,其他从库可以处理读命令。但主库发生故障,就不能处理写命令了,从库只能处理读命令。这就影响服务的正常使用了,该如何解决呢?

只要找一个从库当主库就可以解决了。但还有三个问题需要处理:

  1. 主库真的挂了吗?
  2. 该选择哪个从库作为主库?
  3. 怎么把新主库的相关信息通知给从库和客户端?

这里就要介绍Redis的哨兵机制了。哨兵机制指在Redis主从集群模式下,实现主从库自动切换。它有效地解决了主从集群模式下故障时的三个问题。

哨兵机制的基本流程

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

  • 监控:哨兵进程周期性地给所有主从库发送PING命令,检测它们是否在线运行
  • 选主(选择主库):选出新主库
  • 通知:让从库执行replicaof,与新主库同步;通知客户端,与新主库连接。

通知任务相对比较简单且容易理解,但在监控和选主这两个任务中,哨兵需要做出两个决策:

  • 在监控任务中,哨兵需要判断主库是否处于下线状态;
  • 在选主任务中,哨兵要决定选择哪个从库实例作为主库。

下面就介绍一下这两个任务。

哨兵是如何判断主库是否下线

哨兵对主库的下线状态判断有“主观下线”和“客观下线”两种。

  • 主线下线,哨兵发现主库或从库对PING命令的响应超时。
  • 客观下线,表示主库下线是一个客观事实。

哨兵在判断主从库采用不同方式:

  • 哨兵PING从库,如果超时,就直接标记“主观下线”
  • 哨兵PING主库,超时不能直接标记“主观下线”,因为可能由于网络阻塞等原因导致误判。

如何解决哨兵误判?

通过哨兵集群,也就是由多个哨兵组成的集群来进行判断。采用少数服从多数,超过N/2+1判断主库为“主观下线”,那就判断主库为“客观下线”。关于哨兵集群,下面会详细介绍。

哨兵是如何选定新主库的?

哨兵选定新主库用四个字概括:“筛选+打分”。简单来说,就是根据筛选条件选出候选从库,然后通过打分,选出最高分的作为新主库。下面说一下筛选条件和打分规则。

筛选条件

首先从库必须在线运行。

其次从库网络状态良好。从库和主库断连超出一定的阈值就把这个从库筛掉。这个阈值就是down-after-milliseconds,表示主从库断连的最大连接超时时间。例如发生超过10次,就认定从库的网络状况不好。

关于down-after-milliseconds,值越小,哨兵就越敏感。当网络拥塞但主库正常,可能会发生不必要的切换。而当主库真的故障了,就切换及时,对业务影响最小。

因此down-after-milliseconds要设置合适的值,既减少不必要的切换,也保证能够及时切换,降低对业务的影响。

打分规则

第一轮,优先级最高的从库得分高。可通过slave-priority配置项,设置从库优先级。

第二轮,和旧主库同步程度最接近的从库得分高。很容易理解,越接近主库,说明数据丢失越少。前面介绍主从复制时,已经知道主从库是通过repl_backlog_buffer保持同步的,所以slave_repl_offset最接近master_repl_offset,得分高。

第三轮,ID号小的从库得分高。每个实例都会有一个ID。这是最后一轮,必须决出胜负,所以就选择最小ID的作为新主库。

到这里,我们对哨兵机制的基本流程有了一个整体的认识,下面我们再来了解关于主从切换的两个问题。

在做主从切换时,客户端能否正常地进行请求操作?

如果客户端使用了读写分离,那么读请求不受影响,而写请求会失败。失败持续时间 = 哨兵切换主从的时间 + 客户端感知到新主库的时间。

如果不想让业务感知到异常,那客户端只能把写失败的请求先缓存起来,或者写入消息队列中间件。这种只适合对写入请求返回值不敏感的业务。

应用程序不感知服务中断,哨兵和客户端要做什么?

当哨兵完成主从切换后,客户端需要及时感知到主库发生了变更,然后把缓存的写请求写入到新库中,保证后续写请求不会再受到影响。具体做法有两方面:

一方面是哨兵主要通知客户端。哨兵在选主后,会把新主库的地址写入自己实例的pub/sub里,客户端需要订阅pub/sub。当切换新主库后,客户端就能拿到新主库的地址,把写请求发到这个新主库即可。

另一方面是客户端主动获取最新的主从地址。如果客户端因为某些原因错过了哨兵的通知,或者哨兵通知后客户端处理失败了,就需要客户端主动获取。

下面再来学习哨兵集群。

哨兵集群

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

通常,我们在解决一个系统问题的时候,会引入一个新机制,或者设计一层新功能。这里Redis引入哨兵集群来解决哨兵实例的高可靠性问题。

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

哨兵实例之间可以互相发现,靠的是Redis提供的pub/sub机制,也就是发布/订阅机制。

哨兵和主库建立连接,就可以在主库上发布消息了,比如发布自己的连接信息(IP和端口),同时它也会从主库上订阅消息,获得其他哨兵的连接信息。

当多个哨兵实例都在主库上做了发布和订阅操作,它们之间就能知道彼此的IP地址和端口了。

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

那么,哨兵是如何知道从库的IP地址和端口的呢?

哨兵通过向主库发送INFO命令来完成的。如下图所示:

通过pub/sub机制,哨兵之间可以组成集群;通过INFO命令,哨兵和从库建立连接,并进行监控。

但是哨兵不能只和主、从库连接。因为,主从库切换后,客户端也需要知道新主库的连接信息。所以哨兵还需要把新主库的信息告诉客户端。

还是可以依赖pub/sub机制来完成哨兵和客户端的信息同步。

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

本质上,哨兵就是一个运行在特定模式下的Redis实例,只完成监控、选主和通知的任务,因此它也具有pub/sub功能。下面是哨兵提供的一些重要的频道。

知道这些频道后,可以让客户端从哨兵这里订阅消息了。

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

在哨兵集群模式下,通过“投标仲裁”,选出哨兵Leader来执行主从切换。投标仲裁的流程如下图所示:

任何一个实例判断主库“主观下线”,就会给其他实例发送is-master-down-by-addr命令。

其他实例会根据自己和主库的连接情况,做出Y或N响应。

一个哨兵获得仲裁所需的赞同票数后,就可以标记主库为“客观下线”。这个票数可以通过哨兵配置文件中的quorum配置项来设定。

再给其他哨兵发送命令,表示希望由自己来执行主从切换,并让其他哨兵进行投票。这个也叫“Leader选举”,满足以下两个条件才能当Leader:

  • 拿到半数以上的票数
  • 拿到的票数>=quorum

小结

  • 哨兵机制是实现Redis不间断服务的保证。
  • 哨兵机制的三大任务:监控、选主、通知。
  • 为了降低误判率,通过采用哨兵集群,并采用“少数服从多数”的原则,判断主库是否客观下线。
  • 哨兵集群的关键机制,包括:
    • 基于pub/sub机制的哨兵集群组成过程;
    • 基于INFO命令的从库列表,这可以帮助哨兵和从库建立连接;
    • 基于哨兵自身的pub/sub功能,实现了客户端的哨兵之间的事件通知。
  • 哨兵集群在判断了主库“客观下线”后,经过投票仲裁,选举一个Leader来负责主从切换。

最后再分分享一个经验:要保证所有哨兵实例的配置是一致的,尤其是主观下线的判断值down-after-milliseconds

参考资料

Redis基础篇(七)哨兵机制的更多相关文章

  1. 《【面试突击】— Redis篇》-- Redis的主从复制?哨兵机制?

    能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注左上角编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>-- Redis的主从复制?哨兵机制? 在这个 ...

  2. redis(5)--redis集群之哨兵机制

    哨兵机制 在前面讲的master/slave模式,在一个典型的一主多从的系统中,slave在整个体系中起到了数据冗余备份和读写分离的作用.当master遇到异常终端后,需要从slave中选举一个新的m ...

  3. redis高可用(哨兵机制)

    redis哨兵机制:redis的哨兵系统用于管理多个reids服务器,该系统主要有三个作用: 监控:哨兵 会不断地检查你的主服务(Master)和从服务器(Slave)是否运作正常. 提醒:当被监控的 ...

  4. Redis高可用方案哨兵机制------ 配置文件sentinel.conf详解

    Redis的哨兵机制是官方推荐的一种高可用(HA)方案,我们在使用Redis的主从结构时,如果主节点挂掉,这时是不能自动进行主备切换和通知客户端主节点下线的. Redis-Sentinel机制主要用三 ...

  5. Java基础篇(JVM)——类加载机制

    这是Java基础篇(JVM)的第二篇文章,紧接着上一篇字节码详解,这篇我们来详解Java的类加载机制,也就是如何把字节码代表的类信息加载进入内存中. 我们知道,不管是根据类新建对象,还是直接使用类变量 ...

  6. Redis基础篇(三)持久化:AOF日志

    Redis是内存数据库,但是一旦服务器宕机,内存中的数据将会全部丢失. 最简单的恢复方式是从后端数据库恢复,但这种方式有两个问题: 频繁访问数据库,会给数据库带来巨大的压力: 从数据库中读取相比从Re ...

  7. Redis基础篇(八)数据分片

    现在有一个场景:要用Redis保存5000万个键值对,每个键值对大约是512B,要怎么部署Redis服务呢? 第一个方案,也是最容易想到的,需要保存5000万个键值对,每个键值对约为512B,一共需要 ...

  8. Lua 学习之基础篇七<Lua Module,Package介绍>

    Lua 之Module介绍 包管理库提供了从 Lua 中加载模块的基础库. 只有一个导出函数直接放在全局环境中: [require]. 所有其它的部分都导出在表 package 中. require ...

  9. redis基础篇

    1.redis常见的数据结构 redis是一种以键值对存储的高性能内存数据库,有五种常用的数据类型,string,list,hash,set,zset. 2.redis的过期时间 redis中的key ...

随机推荐

  1. 5.1 Spring5源码--Spring AOP源码分析一

    目标: 1.什么是AOP, 什么是AspectJ, 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP 1.1 什么是 ...

  2. 第2章 Python编程基础知识 第2.1节 简单的Python数据类型、变量赋值及输入输出

    第三节 简单的Python数据类型.变量赋值及输入输出 Python是一门解释性语言,它的执行依赖于Python提供的执行环境,前面一章介绍了Python环境安装.WINDOWS系列Python编辑和 ...

  3. Python中repr(变量)和str(变量)的返回值有什么区别和联系

    Python中repr(变量)和str(变量)都返回一个描述对象的字符串,二者有关联又有不同.由于Python3.0后都是新式类,我们的分析也是基于新式类进行的.基于object派生的新式类中二者之间 ...

  4. PyQt(Python+Qt)学习随笔:Qt Designer中部件的windowIcon属性

    windowIcon对象为部件对象的属性,但只有窗口对象有效,其他派生对象如pushButtong对象无效. 在windowIcon对象上有如下子属性设置: 这几个子属性实际上是QIcon类中继承的. ...

  5. uni与小程序,vue的区别

    标签区别 uni使用小程序的标签,vue使用web端的标签 标签名变化的: 标签描述\类别 vue uniapp 文本 span\font text 链接 a navigator/ router-li ...

  6. Jenkins环境搭建(8)-邮件未能正常发送

    昨天,在使用jenkins构建项目时,出现了个问题,问题是:jenkins控制台日志显示邮件发送成功,但实际没有成功. 此前,jenkins的配置,项目构建后,是能正常发送邮件的,可这次突然就不行了, ...

  7. CF1327F AND Segments

    链接 Description 要求构造满足下列条件的长度为 \(n\) 的序列 \(a\) 的个数: 每个数值域在 \([0, 2 ^ k)\) \(m\) 个限制条件 \(l, r, x\),需要满 ...

  8. AcWing 380. 舞动的夜晚

    大型补档计划 题目链接 这题是求必须边,而不是不可行边,因为不可行边 = 必须边 + 死掉了的边(貌似lyd第三版书上还是说的不可行边)先跑最大流. 在跑完以后的残余网络上,对于一条当前匹配的边 \( ...

  9. Codeforces Edu Round 58 A-E

    A. Minimum Integer 如果\(d < l\),则\(d\)满足条件 否则,输出\(d * (r / d + 1)\)即可. #include <cstdio> #in ...

  10. STL——容器(List)list 的反序排列

    list.reverse(); //反转链表,比如list包含1, 2, 3, 4, 5五个元素,运行此方法后,list就包含5, 4, 3, 2, 1元素. 1 #include <iostr ...