Redis系列 —— (四)Redis的高可用

Redis系列 —— (四)Redis的高可用

Redis——一个当下项目中几乎无所不在的基于内存的key-value数据库。

每每谈到 Redis ,总有一些绕不过去点,比如 Redis 为什么快、如何数据持久化、如何搭建高可用集群等等。

Redis 系列是有本人一边学习 Redis 底层原理一边记录的学习日记。希望对大家有所启发。

在文章开头,我们先从几个问题入手。

为什么要高可用

1.解决有可能出现的数据丢失问题(数据尽量少丢失)
2.解决有可能出现的服务中断问题(服务尽量少中断)

如何高可用

搭建多实例集群,设置主从库,读写分离、哨兵机制等等。

下面将根据以上几点问题逐步展开。

我们总说 Redis 具有高可靠性,那么是具体是如何做到的呢?

可以从两个方面来说,一个是数据尽量少丢失,一个是服务尽量少中断。

第一个我们可以通过 AOFRDB 来保证,后者呢,可以通过增加副本冗余,也就是增加 Redis 实例,将一份数据同时保存在多个实例上,避免因为实例中断影响业务使用。

但是多个实例上保存同一份数据,就会出现数据一致性的问题。Redis 是如何解决的呢?

实际上,Redis 提供了主从库模式,以保证各实例之间数据一致,主从库采用读写分离的方式。

  • 读操作 主库从库都可以接收
  • 写操作 只有主库接收,然后主库复制数据到从库进行同步
    在这里插入图片描述

那为什么主从库之间要采用读写分离呢?

我们可以想象一下,如果写操作可以写到任意从库中,对同一个数据 K1 进行三次修改,分别写到不同从库中,读取 K1 这个数据就有可能出现旧址,产生脏读。当前我们可以通过家加锁等操作来保证数据的一致性,但是频繁的写操作会带来巨额的开销,增加复杂性,所以是不太能接受的。

而主从库之前采用读写分离的方式,所有写操作只在主库运行,不同协调从库,主库再复制数据到其他从库上,达到数据一致。

主从同步 读写分离

主从库是如何进行同步的

当我们启动多个 Redis 实例时,它们可以通过 replicaof 命令形成主从关系。

例如现在有实例1(172.16.19.3)和实例2(172.16.19.5),在实例2上执行命令后,实例2就是实例1的从库

replicaof 172.16.19.3 6379

在这里插入图片描述
上图是主从库之间第一次同步的三个阶段。

第一阶段是主从库之间建立连接,协商同步的过程,主要是为全量复制做准备。

第二阶段是主库生成 RDB 快照文件发送给从库,从库接收到后先清空数据然后进行全量复制

第三阶段是通过把第二阶段执行中主库接收到的写操作发送给从库。主库生成 RDB 快照文件后,后续的写操作会写到主库的 replication buffer 中,然后发送给从库,从而做到主从库数据一致。

主从复制会有一个问题,如果从库很多,主库多次 fork 子进程生成 RDB 文件和传输 RDB 文件的操作会占用主进程处理正常请求,所以可以使用 主–从--从 的方案。就是分级从库,分摊主库压力。如下图:

在这里插入图片描述
上述的主从复制方案是从库基于主库的 RDB 快照文件进行复制的,但是我们知道,Redis 数据持久化有两种操作,一种是 RDB ,另一种是 AOF 。

那么为什么主从复制的操作不是基于 AOF 来实现的呢?

因为 RDB 文件是经过压缩的二进制数据文件,文件很小。而 AOF 是记录每一次写操作命令到文件中,写操作越多文件就越大。而且会有很多针对同一个 key 的冗余操作存在。在主从全量复制时,RDB 文件的传输和可以尽量降低主库实例对带宽的消耗,而从库也可以更快的加载数据。而主库传输 AOF 文件和从库逐步执行 AOF 文件中的写操作时,都比 RDB 的方式慢得多,所以使用 RDB 方式进行主从全量复制的成本最低。

主从复制的整个过程是存在着风险的,比如常见的网络断连或阻塞。如果网络断连,就会出现主从数据不一致,出现脏读。

主从间网络断连怎么办?

Redis 2.8 之前,主从库之间断连,会进行一次全量复制,开销很大。
Redis 2.8 之后,主从库会采用增量复制的方式进行同步。增量复制只会把断连期间的写操作同步给从库。

那增量复制是如何实现的呢

主从库关系绑定后,主库会在内存创建 repl_backlog_buffer 缓冲区,主从库断连恢复后,主库会在 repl_backlog_buffer 缓冲区中找到需要同步的数据给从库同步。

在这里插入图片描述
如上图所示,repl_backlog_buffer 是一个环形缓冲区,主库记录自己写到的位置 master_repl_offset ,从库记录自己读到的位置 slave_repl_offset ,随着不断记录写的位置,会产生偏移量 ,主从库恢复连接后,从库发送自己的读位置给主库,主库根据自己的写位置和从库的读位置来判断需要同步的数据。

但是整个过程有一个问题,就是如果长时间断连, repl_backlog_buffer 环形缓冲区的读位置有可能被写位置覆盖,那么就需要进行一次全量复制。也可以适当调整缓冲区的大小,尽量避免这种情况。

以上解决了第一个问题(数据尽量少丢失)


下面我们要解决第二个问题(服务尽量少中断)

哨兵机制

在主从库集群的模式下,多个从库可以让 Redis 保持更高的可用性,从库的故障不会影响整个 Redis 服务,其他从库和主库依然可以继续接受读写操作。但是如果主库发生了故障,从库可以继续提供读服务,但是写服务就会中断,而且从库无法继续同步主库的数据,读服务提供的数据也没有可靠性的保证。

在这里插入图片描述
如果主库故障,那么就需要选择一个新的主库继续提供读服务。

这里就不得不提 Redis 的哨兵机制了,在 Redis 主从集群中,哨兵机制是实现主从库自动转换的关键机制。

哨兵机制的基本流程

哨兵机制其实就是运行在特殊模式下的 Redis 进程,主从实例运行的同时,它也在运行。

哨兵主要负责三个任务:

  • 监控
    哨兵运行进程时,会周期性的给所有主从库发送 ping 命令检测他们是否依然在线。如果从库没有在规定时间内响应哨兵的 ping 命令,哨兵就会标注该从库为下线状态,如果主库呗标记为下线状态,就会开始自动切换主库过程。
  • 选主
    主库挂了后,哨兵会根据一定规则,在从库里选择一个作为新的主库。
  • 通知
    选举了新主库后,哨兵会把新主库的连接信息发送给其他从库,让他们执行 replicaof 命令,和新主库创建连接并复制数据。同时,把新主库的连接信息通知给客户端,请求到新主库上。
    在这里插入图片描述
主观下线和客观下线

哨兵进程会通过 ping 命令来检测它和主从库之间的连接状况,如果检测到响应超时,则标注为”主观下线“。

如果检测出故障的是从库,那就标记”主观下线”即可。但如果是检测到主库出故障,标记为“主观下线”后还不能进行选主操作。因为哨兵有可能出现误判,导致没必要的选主和通知操作造成额外的计算和通信开销。

为了避免哨兵的误判,哨兵机制也会采用集群模式部署。通过引入多个哨兵实例经过投票的方式做决策。
在这里插入图片描述
那哨兵是如何通过投票方式做决策呢?

任何一个哨兵实例只要自身判断主库“主观下线”后,会像其他哨兵实例发送 is-master-down-by-addr 。其他哨兵实例会根据自己检测到的主库连接情况作出响应,Y 相当于赞成票,N 相当于反对票。一个哨兵获得了仲裁所需的赞成票后,标记主库为“客观下线”。

这个所需赞成票是由配置文件中的 quorum 配置项设定的。例如有 5 个哨兵实例,quorum 配置为 3 ,那哨兵标记主库为“客观下线”就需要包括它自己的 3 个赞成票。

主库被标记为“客观下线”,就可以进行下一个决策过程了,即从其余从库中选择一个新的主库。
在这里插入图片描述

如何选择新主库

一般来说,我们把哨兵选择主库的过程成为“筛选+打分”。简单来说,是先按照一定的筛选条件过滤掉不符合条件的从库,再按照一定的规则给剩余的从库逐个打分,打分最高的被选为主库。如下图所示:

在这里插入图片描述

主要关注筛选条件和规则。
首先,我们看筛选条件,除了要检测从库的当前在线状态,还要判断它之前的网络连接情况,如果从库总是和主库断连,而且断连的次数超过一定的阔值,我们就有理由相信这个从库的网络张太不好,从而把这个从库筛选掉。

具体使用配置项(从库断连的最大超时时间)
down-after-milliseconds * 10
按照上面的配置,如果发生断连的次数超过 10 次,就说明这个从库网络状况不好,不适合做主库,否则要频繁做选主操作。

接下来,我们就要给剩余的从库进行打分。
我们分别按照三个规则进行三次打分,分别是:

  • 从库优先级
  • 主从复制进度
  • 从库ID

第一轮:优先级最高的从库得分高
可以通过 slave-priority 配置项来给从库设置优先级,比如给内存大的从库设定高优先级,如果优先级一致,则进入下一轮打分。

第二轮:和旧主库同步程度最接近的从库得分高
如何判断主从库的同步进度呢?
还记得前面讲主从复制的时候的 repl_backlog_buffer 缓冲区吗?在缓冲区中找到从库的 slave_repl_offset 和旧主库的 master_repl_offset ,那个从库的 slave_repl_offset 最接近旧主库的 master_repl_offset ,那个从库就得分高。

就像下图所示,旧主库的 master_repl_offset 是 1000,从库 1、2 和 3 的 slave_repl_offset 分别是 950、990 和 900,那么,从库 2 就应该被选为新主库。

在这里插入图片描述
如果从库的主从复制进度一样,打分一样高,那就进入下一轮打分。

第三轮:ID号小的从库得分高
目前,Redis 在选主库时,有一个默认的规则,再优先级和复制进度都相同的情况下,ID 号小的从库得分最高,会被选为新主库。

如何通知

上一步中,新的主库已经通过选主过程选择出来。
那么接下来就是通知其他从库和新主库创建连接并复制数据,通知客户端,让客户端的写操作执行到新的主库实例。

那么具体是怎么通知的呢?

部署哨兵集群时会发现,我们只用到下面这个配置项设置主库的 IP 和端口,并没有其它哨兵的连接信息。

sentinel monitor <master-name> <ip> <redis-port> <quorum>

那哨兵之间是如何通信的呢?
哨兵实例之间可以相互发现,是基于 Redis 提供的 Pub/Sub 机制,也就是发布/订阅 机制

哨兵只要和主库建立起连接,就可以在主库发布自己的信息,也可以从主库订阅消息,获得其他哨兵实例的信息,从而做到哨兵集群的互相通信。

如下图所示,在主从集群中,主库上有一个名为“sentinel:hello”的频道,不同哨兵就是通过它来互相发现,实现互相通信的。

在这里插入图片描述

哨兵除了互相发现外,还要知道从库的连接信息,从而实现对从库的心跳监测。

这是由哨兵向主库发送 INFO 命令来完成的。就像下图所示:
在这里插入图片描述

以上哨兵做到了互相发现和对主从的心跳监测接下来,接下来还要通知客户端新主库的信息。我们仍然可以通过 Pub/Sub 机制帮助我们完成哨兵和客户端之间的信息同步。

当哨兵选出新主库后,客户端就会看到下面的 switch-master 事件。这个事件表示主库已经切换了,客户端就可以使用新主库信息进行通信。

switch-master <master name> <oldip> <oldport> <newip> <newport>

由哪个哨兵执行主从切换

上面的过程都完成后,就剩下一个问题了,哨兵是个集群,到底由哪个哨兵执行主从切换呢?

之前在选主的过程中,我们有过“投票冲裁”的方式,其实到底由哪个哨兵执行主从切换也是类似的方式。

选主过程中,有哨兵检测到主库故障,标记为“主观下线”,然后通过投票标记为“客观下线”后,就会继续向其他哨兵发送命令,请求由自己执行主从切换,并让其他哨兵投票。这个投票的过程称为“Leader选举”。

在投票过程中,任何一个想成为 Leader 的哨兵,要满足两个条件:第一,拿到半数以上的赞成票;第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。

如果哨兵Leader选举没有满足上面的两个条件,那么这轮投票就不会产生 Leader。哨兵集群会等待一段时间(也就是哨兵故障转移超时时间的 2 倍),再重新选举。这是因为,哨兵集群能够进行成功投票,很大程度上依赖于选举命令的正常网络传播。如果网络压力较大或有短时堵塞,就可能导致没有一个哨兵能拿到半数以上的赞成票。所以,等到网络拥塞好转之后,再进行投票选举,成功的概率就会增加。

需要注意的是,如果哨兵集群只有 2 个实例,此时,一个哨兵要想成为 Leader,必须获得 2 票,而不是 1 票。所以,如果有个哨兵挂掉了,那么,此时的集群是无法进行主从库切换的。因此,通常我们至少会配置 3 个哨兵实例。这一点很重要,你在实际应用时可不能忽略了。

最后,分享一个经验:要保证所有哨兵实例的配置是一致的,尤其是主观下线的判断值 down-after-milliseconds。因为这个值在不同的哨兵实例上配置不一致,导致哨兵集群一直没有对有故障的主库形成共识,也就没有及时切换主库,最终的结果就是集群服务不稳定。所以,你一定不要忽略这条看似简单的经验

以上是关于Redis高可用的整理。希望对你有用!

在最后的最后,要特别推荐大家看一下 极客时间上由蒋德钧教授授课的《Redis核心技术与实战》。保证让你受益匪浅。

我还在路上,我将继续走下去…

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Justin-D

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值