怎么实现Redis的高可用?(主从、哨兵、集群)

高可用有两个含义: 一是数据尽量不丢失,二是保证服务尽可能可用。 AOF 和 RDB 数据持久化保证了数据尽量不丢失,那么多节点来保证服务尽可能提供服务。

一般在实际生产中,服务不会部署成单节点,主要是有三个原因.

为了实现高可用,通常的做法是,将数据库复制多个副本以部署在不同的服务器上,其中一台挂了也可以继续提供服务。Redis 实现高可用有三种部署模式: 主从模式哨兵模式

集群模式

一、主从模式

既然一台服务宕机了会导致提供不可用,那是不是可以考虑多台就可以解决了。Redis 提供了主从模式。通过主从复制,将数据冗余一份复制到其他 Redis 服务器。

Master节点,负责读写操作,Slave节点,只负责读操作

主从模式采用了读写分离,所有数据的写操作只会在Master库上进行,Master库有了最新的数据后,会同步给Slave库,这样,主从库的数据就是一致的。

这里要思考是主从库同步是如何完成的?Master库数据是一次性传给Slave库,还是分批同步的?正常运行中又怎么同步呢?要是主从库间的网络断连了,重新连接后需要再次全量同步还是只需部分同步呢?

主从复制包括全量复制,增量复制两种。redis2.8版本之后还支持部分同步。

(1) 全量同步

一般当Slave第一次启动连接Master,认为是第一次连接,就采用全量复制,全量复制流程如下:

完成上面几个步骤后就完成了Salve节点初始化的所有操作,Slave服务器此时可以接收来自用户的读请求。

redis2.8版本之后,已经使用psync来替代sync,因为sync命令非常消耗系统资源,而且不支持部分同步,psync的效率更高,支持部分同步,有关部分同步下面细说。

(2) 增量同步

Redis增量复制是指Slave初始化后开始正常工作时,Master服务器发生的写操作同步到Slave服务器的过程。

增量复制的过程主要是Master服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

(3) 部分同步

在redis 2.8版本之前,并不支持部分同步,当主从服务器之间的连接断掉之后,Master服务器和Slave服务器之间必须进行全量数据同步,此时Slave服务器会清空所有数据,

再次加载Master的RDB文件。但是从redis 2.8开始,即使主从连接中途断掉,也不一定需要进行全量同步,它可以支持部分同步,来提高效率。

它的工作原理大致是这样:

通过这个图我们再来理解下 为什么2.8部分可以实现部分同步

  • 如果Slave节点从未执行过 replicaof,则Slave节点发送 psync ? -1,向Master节点发送全量复制请求;
  • 如果Slave节点之前执行过 replicaof 则发送 psync <runid> <offset></offset></runid>, runID 是上次复制保存的Master节点 runID,offset 是上次复制截至时Slave节点保存的复制偏移量。

  • runID 与Slave节点发送的 runID 相同,且Slave节点发送的 slave_repl_offset 之后的数据在 repl_backlog_buffer 缓冲区中都存在,则回复 CONTINUE,表示将进行

部分复制,Slave节点等待Master节点发送其缺少的数据即可;

  • runID 与Slave节点发送的 runID 不同,或者Slave节点发送的 slave_repl_offset 之后的数据已不在Master节点的 repl_backlog_buffer缓冲区中 (在队列中被挤出了),

则回复Slave节点 FULLRESYNC <runid> <offset></offset></runid>,表示要进行全量复制,其中 runID 表示Master节点当前的 runID,offset 表示Master节点当前的 offset,Slave节点

保存这两个值,以备使用。

一个Slave库如果和Master库断连时间过长,造成它在Master库 repl_backlog_buffer的 slave_repl_offset 位置上的数据已经被覆盖掉了,此时Slave库和Master库间将进行

全量复制。

  • 做到读写分离,提高服务器性能。Salve可以分载Master的读操作压力,当然写服务依然必须由Master来完成;
  • 当Master节点服务挂了,可以让Slave变成Master节点继续提供服务;

  • 在主从模式中,一旦Master节点由于故障不能提供服务,需要人工将Slave节点晋升为Master节点,同时还要通知应用方更新Master节点地址。显然,多数业务场景都不能接受这种故障处理方式;

  • redis的Master节点和Slave节点中的数据是一样的,降低的内存的可用性,而且存储能力也有限。
  • 主从复制写还都是在Master节点,所以写的压力并没有减少。

因此,主从复制其实并不能满足我们高可用的要求。

二、哨兵模式

在主从模式中,一旦Master节点由于故障不能提供服务,需要人工将Slave节点晋升为Master节点。显然,多数业务场景都不能接受这种故障处理方式。Redis从2.8开始正式提供了

Redis Sentinel(哨兵)架构来解决这个问题。

&#x54E8;&#x5175;&#x6A21;&#x5F0F;,由一个或多个Sentinel实例组成的Sentinel系统,它可以监视所有的Master节点和Slave节点,并在被监视的Master节点进入下线状态时,自动将下线Master服务器

属下的某个Slave节点升级为新的Master节点。但是呢,一个哨兵进程对Redis节点进行监控,就可能会出现问题(单点问题),因此,可以使用多个哨兵来进行监控Redis节点,

并且各个哨兵之间还会进行监控。

sentinel是一种特殊的redis实例,它不存储数据,只对集群进行监控。

简单来说,哨兵模式就三个作用:

哨兵主要有三个定时监控任务完成对各节点的发现和监控。

任务1:每个哨兵节点每 10 秒会向Master节点和Slave节点发送 info 命令获取最拓扑结构图,哨兵配置时只要配置对Master节点的监控即可,通过向Master节点发送info,

获取Slave节点的信息,并当有新的Slave节点加入时可以马上感知到

任务2,每个哨兵节点每隔 2 秒会向redis 数据节点的指定频道上发送该哨兵节点对于Master节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解

其它哨兵节点的信息及对Master节点的判断,其实就是通过消息publish 和subscribe 来完成的;

任务3,每隔 1 秒每个哨兵会向Master节点、Slave节点及其余哨兵节点发送一次ping 命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据

这里可以分为 哨兵主观下线 和 哨兵客观下线

&#x54E8;&#x5175;&#x4E3B;&#x89C2;&#x4E0B;&#x7EBF;

上面说过哨兵节点每隔 1 秒对Master节点和Slave节点、其它哨兵节点发送 ping 做心跳检测,当这些心跳检测时间超过 down-after-milliseconds 时,哨兵节点则认为

该节点 错误或下线,这叫主观下线;

当然这但不代表这个master真的不能用(有可能网络波动导致该哨兵与服务连接异常),所以主观下线是不可靠的,可能存在误判。

&#x54E8;&#x5175;&#x5BA2;&#x89C2;&#x4E0B;&#x7EBF;

当主观下线的节点是Mater节点时,此时该哨兵 节点会通过指令 sentinelis-masterdown-by-addr 寻求其它哨兵节点对Master节点的判断,当超过quorum(法定人数)

个数,此时哨兵节点则认为该Master节点确实有问题,这样就客观下线了,大部分哨兵节点都同意下线操作,也就说是客观下线

如果哨兵客观下线某Master,那是不是接下来要选举新的Master,这个工作只要一个哨兵完成即可,所以首先要做的是选举一个哨兵领导者。

1) &#x9886;&#x5BFC;&#x8005;&#x9009;&#x4E3E;

原因:只有一个sentinel节点完成故障转移所以需要选举。 选举通过sentinelis-master-down-by-addr命令希望成为领导者:

2) &#x5728;&#x4ECE;&#x8282;&#x70B9;&#x4E2D;&#x9009;&#x62E9;&#x65B0;&#x7684;Master&#x8282;&#x70B9;

sentinel状态数据结构中保存了主服务的所有从服务信息,领头sentinel按照如下的规则从从服务列表中挑选出新的主服务

3) &#x66F4;&#x65B0;&#x4E3B;&#x4ECE;&#x72B6;&#x6001;

通过slaveof no one命令,让选出来的Slave节点成为Master节点;并通过slaveof命令让其他节点成为其Slave节点。

将已下线的Master节点设置成新的Master节点的Slave节点,当其回复正常时,复制新的Master节点,变成新的Master节点的Slave节点

1) &#x4EC0;&#x4E48;&#x662F;&#x8111;&#x88C2;

脑裂 也就是说,某个 Master 所在机器突然脱离了正常的网络,跟其他 slave 机器不能连接,但是实际上 master 还运行着。此时哨兵可能就会认为 master 宕机了,然后开启

选举,将其他 slave 切换成了 master。这个时候,集群里就会有 两个 Master ,也就是所谓的脑裂。

此时虽然某个 slave 被切换成了 master,但是可能 client 还没来得及切换到新的 master,还继续向旧 master 写数据。因此旧 master 再次恢复的时候,会被作为一个 slave

挂到新的 master 上去,自己的数据会清空,重新从新的 master 复制数据。而新的 master 并没有后来 client 写入的数据,因此,这部分数据也就丢失了。

2) &#x5982;&#x679C;&#x89E3;&#x51B3;&#x8111;&#x88C2;&#x95EE;&#x9898;

Redis 已经提供了两个配置项来限制Master库的请求处理,分别是 min-slaves-to-writemin-slaves-max-lag。 (2.8以后改为min-replicas-to-write 和 min-replicas-max-lag )

min-slaves-to-write 1
min-slaves-max-lag 10

如上两个配置:要求至少有 1 个 slave,数据复制和同步的延迟不能超过 10 秒,如果超过 1 个 slave,数据复制和同步的延迟都超过了 10 秒钟,那么这个时候,master 就

不会再接收任何请求了。

这样一配置的话,就算你的Master库是假故障,那它在假故障期间也无法响应哨兵心跳,也不能和Slave库进行同步,自然也就无法和Slave库进行 ACK 确认了。原Master库就

会被限制接收客户端请求,客户端也就不能在原Master库中写入新数据了。

当然这个配置做不到让数据一点也不丢失,而是让数据尽可能的少丢失。

三、Cluster集群模式

先说一个误区: Redis的集群模式本身没有使用一致性hash算法,而是使用slots插槽。因为我查了很多资料都是这么说的,至于为什么使用slots插槽,我个人理解slots插槽多少

个是固定的,这样更加方便数据迁移。

哨兵模式基于主从模式,实现读写分离,它还可以自动切换,系统可用性更高。但是它每个节点存储的数据是一样的,浪费内存。因此,在Redis3.0后Cluster集群应运而生,

它实现了Redis的分布式存储。对数据进行分片,也就是说 每台Redis节点上存储不同的内容,来解决在线扩容的问题。

一个Redis Cluster由多个Redis节点构成,节点组内部分为主备两类节点,对应master和slave节点。两者数据准实时一致,通过异步化的主备复制机制来保证。

一个节点组有且只有一个master节点,同时可以有0到多个slave节点,在这个节点组中只有master节点对用户提供些服务,读服务可以由master或者slave提供。如上图中,

包含三个master节点以及三个master对应的slave节点,一般一组集群至少要6个节点才能保证完整的高可用。

其中三个master会分配不同的slot(表示数据分片区间),当master出现故障时,slave会自动选举成为master顶替Master节点继续提供服务。

一个或多个Slave节点;

  • Master1负责0~5460号哈希槽
  • Master2负责5461~10922号哈希槽
  • Master3负责10922~16383号哈希槽

redis 集群中Master节点故障处理方式与哨兵模式较为相像,当约定时间内某节点无法与集群中的另一个节点顺利完成ping消息通信时,则将该节点标记为主观下线状态,同时

将这个信息向整个集群广播。

如果一个节点收到某个节点失联的数量达到了集群的大多数时,那么将该节点标记为客观下线状态,并向集群广播下线节点的fail消息。然后立即对该故障节点进行主从切换。

等到原来的Master节点恢复后,会自动成为新Master节点的Slave节点。 如果Master节点没有Slave节点,那么当它发生故障时,集群就将处于不可用状态。

在cluster中我们如何动态上线某个节点呢。当集群中加入某个节点时,哈希槽又是如何来进行分配的?当集群中加入新节点时,会与集群中的某个节点进行握手,该节点会把集群

内的其它节点信息通过gossip协议发送给新节点,新节点与这些节点完成握手后加入到集群中。

然后集群中的节点会各取一部分哈希槽分配给新节点,如下图:

  • Master1负责1365-5460
  • Master2负责6827-10922
  • Master3负责12288-16383
  • Master4负责0-1364,5461-6826,10923-12287

当集群中要删除节点时,只需要将节点中的所有哈希槽移动到其它节点,然后再移除空白(不包含任何哈希槽)的节点就可以了。

有关gossip协议这里需要单独解释下

在整个redis cluster架构中,如果出现以下情况

  • 新加入节点
  • slot迁移
  • 节点宕机
  • slave选举成为master

我们希望这些变化能够让整个集群中的每个节点都能够尽快发现,传播到整个集群并且集群中所有节点达成一致,那么各个节点之间就需要相互连通并且携带相关状态数据进行

传播,按照正常的逻辑是采用广播的方式想集群中的所有节点发送消息,有点是集群中的数据同步较快,但是每条消息都需要发送给所有节点,对CPU和带宽的消耗过大,所以

这里采用了gossip协议。

Gossip protocol 也叫 Epidemic Protocol( &#x6D41;&#x884C;&#x75C5;&#x534F;&#x8BAE;),别名很多比如:”流言算法”、”疫情传播算法”等。

时间内会达成一致,如下图所示。

假设我们提前设置如下规则:

1、Gossip 是周期性的散播消息,把周期限定为 1 秒

2、被感染节点随机选择 k 个邻接节点(fan-out)散播消息,这里把 fan-out 设置为 3,每次最多往 3 个节点散播。

3、每次散播消息都选择尚未发送过的节点进行散播

4、收到消息的节点不再往发送节点散播,比如 A -> B,那么 B 进行散播的时候,不再发给 A。

这里一共有 16 个节点,节点 1 为初始被感染节点,通过 Gossip 过程,最终所有节点都被感染:

gossip协议包含多种消息,包括ping,pong,meet,fail等等。

ping:每个节点都会频繁给其他节点发送ping,其中包含自己的状态还有自己维护的集群元数据,互相通过ping交换元数据;

pong: 返回ping和meet,包含自己的状态和其他信息,也可以用于信息广播和更新;

fail: 某个节点判断另一个节点fail之后,就发送fail给其他节点,通知其他节点,指定的节点宕机了。

meet:某个节点发送meet给新加入的节点,让新节点加入集群中,然后新节点就会开始与其他节点进行通信,不需要发送形成网络的所需的所有CLUSTER MEET命令。

&#x4F18;&#x70B9;: gossip协议的优点在于元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新有一定的延时,降低了压力; 去中心化、可扩展、

容错、一致性收敛、简单。 由于不能保证某个时刻所有节点都收到消息,但是理论上最终所有节点都会收到消息,因此它是一个最终一致性协议。

&#x7F3A;&#x70B9;: 元数据更新有延时可能导致集群的一些操作会有一些滞后。 消息的延迟 , 消息冗余 。

声明: 公众号如需转载该篇文章,发表文章的头部一定要 告知是转至公众号: 后端元宇宙。同时也可以问本人要markdown原稿和原图片。其它情况一律禁止转载!

Original: https://www.cnblogs.com/qdhxhz/p/15702347.html
Author: 雨点的名字
Title: 怎么实现Redis的高可用?(主从、哨兵、集群)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/528962/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Docker 容器虚拟化

    Docker 容器虚拟化 1、虚拟化网络 Network Namespace 是 Linux 内核提供的功能,是实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,它们有独自网络…

    Linux 2023年6月7日
    0141
  • js笔记之while循环

    while循环就是for循环去掉前后两个条件 var i = 0; for( ; i < 10; ){ document.write(i); document.write(&…

    Linux 2023年6月13日
    0107
  • VMware 和 Linux 的安装

    常见的虚拟机软件有 VMware Workstation(简称 VMware)、VirtualBox、Microsoft Virtual PC 等,本文以 VMware 为例来讲解…

    Linux 2023年5月27日
    090
  • Netty源码解读(二)-服务端源码讲解

    简单Echo案例 代码是netty的源码,我添加了自己理解的中文注释。 了解了Netty的线程模型和组件之后,我们先看看如何写一个简单的Echo案例,后续的源码讲解都基于此案例。以…

    Linux 2023年6月7日
    0132
  • Linux系统卡死后紧急处理

    前言:Linux系统卡死了的情况有很多,最常见的是系统负载过高导致的。还可以运行内存耗用极大的程序(如虚拟机),也会迅速提升系统负载。注意:不能再试图依赖任何图形界面的东西,如 G…

    Linux 2023年6月7日
    0105
  • Mysql 安全加固经验总结

    本文为博主原创,转载请注明出处: 1.内网部署Mysql mysql 数据库在使用过程中,需要给服务提供连接和访问的权限,而不需要进行公网连接和访问,所以在安全环境和现网环境部署m…

    Linux 2023年6月14日
    099
  • 编译iPhone静态库的Makefile实例(与Shell脚本一起完成)

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/cy568searchx/p/5735419.htmlA…

    Linux 2023年5月28日
    0116
  • 手把手教你在Linux系统下安装MongoDB

    1. 下载最新的stable版MongoDB [root@spirit-of-fire ~]# wget http://downloads.mongodb.org/linux/mo…

    Linux 2023年6月14日
    0141
  • OpenSSH制作rpm包和升级OpenSSH过程中遇到的问题

    百度网盘:https://pan.baidu.com/s/1gqpH2xeOkYHJ0CiztbmqoQ 提取码:imfg cp x11-ssh-askpass-1.2.4.1.t…

    Linux 2023年6月7日
    092
  • 记一次burp suite文件上传漏洞实验

    一·文件上传漏洞概念文件上传漏洞是指 Web 服务器允许用户在没有充分验证文件名称、类型、内容或大小等内容的情况下将文件上传到其文件系统。未能正确执行这些限制可能意味着即使是基本的…

    Linux 2023年6月7日
    0108
  • Linux下使用ssh测试端口是否开启

    当服务器上不允许使用telnet时,可以使用ssh测试远程服务器端口是否开启 具体命令如下 -v 显示连接debug信息 -p port 指定端口 ssh -v -p 80 roo…

    Linux 2023年6月7日
    0116
  • 数据结构简单话(一)线性表

    前言 逻辑结构 物理存储结构 一、顺序表 二、链表 总结 前言 本菜鸟笔者打算入门一下数据结构,在学习过程中通过自己简单话术总结相关基础知识要点,希望能帮助同样在入门的小伙伴们快速…

    Linux 2023年6月7日
    0133
  • python学习(解析python官网会议安排)

    对html的解析是网页抓取的基础,分析抓取的结果找到自己想要的内容或标签以达到抓取的目的。 HTMLParser是python用来解析html的模块。它可以分析出html里面的标签…

    Linux 2023年6月14日
    0110
  • PYTORCH: 60分钟 | TENSORS

    Tensor是一种特殊的数据结构,非常类似于数组和矩阵。在PyTorch中,我们使用tensor编码模型的输入和输出,以及模型的参数。Tensor类似于Numpy的数组,除了ten…

    Linux 2023年6月16日
    0185
  • 微信白名单获取

    公司在微信WIFI登录项目中,需要在BRAS设备上添加微信服务器的IP地址到白名单列表中,以实现用户连接热点后,能够使用微信中的功能(如:添加公众号),但不能使用其他应用进行上网操…

    Linux 2023年6月14日
    0115
  • Python中import外部模块全局变量修改规则及踩坑

    最近碰到一个import外部文件全局变量修改后未符合预期效果的问题,简要描述如下: 有env.py, test.py, dal.py三个文件,env.py 中定义了DEBUG=Fa…

    Linux 2023年6月6日
    079
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球