这是我参与「日新方案 12 月更文挑战」的第5天,点击检查活动概况

前言

前几天我在知乎看到一个问题:怎么树立自己的常识体系和观念?[1]

Redis高可用全景一览

在一篇高赞回答中讲述了树立“外脑”是关键,文章观念以为:大脑是用来考虑的,不是用来记忆的。

Redis高可用全景一览

我很认同这样的观点,我的账号名为“杨同学technotes”,technotes[2] 源于我最近几年总结的 github 项目,意为“技能笔记”,这便是我的“外脑”。我将一直往里面填充东西,不断优化内容,欢迎重视。

  • [1] www.zhihu.com/question/52…
  • [2] www.dbses.cn/technotes

本文来自我的 technotes Redis篇。

正文

关于一项技能的学习,咱们要对这项技能有一个大局观,下面是一张 Redis 全景图,我觉得画得十分全面。

Redis高可用全景一览

今天咱们主要重视 Redis 的高可用主线。Redis 的高可用性,详细来说,有两方面意义:一是服务少中止,二是数据少丢掉。

为了确保服务少中止,一般的做法便是冗余,增加服务的副本,可是当副本多了今后,怎么确保副本的数据共同就成了问题。

一、主从库方式

在这方面,Redis 供给了主从库方式,主从库之间选用的是读写别离的办法:关于读操作恳求,主从库都能够接纳;关于写操作,首先到主库履行,然后,主库将写操作同步给从库。

Redis高可用全景一览

那么,主从库同步是怎么完结的呢?主库数据是一次性传给从库,仍是分批同步?要是主从库间的网络断连了,数据还能保持共同吗?

1.1 数据同步的完成细节

当咱们启动多个 Redis 实例的时分,它们相互之间就能够经过 replicaof(Redis 5.0 之前运用 slaveof)指令构成主库和从库的关系。

例如,现在有实例 1(ip:172.16.19.3)和实例 2(ip:172.16.19.5),咱们在实例 2 上履行以下这个指令后,实例 2 就变成了实例 1 的从库,并从实例 1 上仿制数据:

replicaof 172.16.19.3 6379

之后会依照三个阶段完结数据的第一次同步。

第一阶段:主从库间树立连接、协商同步的进程,主要是为全量仿制做准备。详细来说,从库给主库发送 psync 指令,表明要进行数据同步,主库根据这个指令的参数来启动仿制。psync 指令包含了主库的 runID 和仿制进展 offset 两个参数。runID,是每个 Redis 实例启动时都会主动生成的一个随机 ID,用来仅有符号这个实例。当从库和主库第一次仿制时,由于不知道主库的 runID,所以将 runID 设为“?”。offset,此刻设为 -1,表明第一次仿制。

主库收到 psync 指令后,会用 FULLRESYNC 呼应指令带上两个参数:主库 runID 和主库现在的仿制进展 offset(这个offset是当时最新的值),回来给从库。从库收到呼应后,会记载下这两个参数。

第二阶段:主库将所有数据同步给从库。详细来说,主库履行 bgsave 指令,生成 RDB 文件,接着将文件发给从库。从库接纳到 RDB 文件后,会先清空当时数据库,然后加载 RDB 文件。

为什么要先清空当时数据库呢?这是由于从库在经过 replicaof 指令开端和主库同步前,或许保存了其他数据。为了防止之前数据的影响,从库需要先把当时数据库清空。

在主库将数据同步给从库的进程中,主库不会被堵塞,依然能够正常接纳恳求。否则,Redis 的服务就被中止了。可是,这些恳求中的写操作并没有记载到刚刚生成的 RDB 文件中。为了确保主从库的数据共同性,主库会在内存顶用专门的 replication buffer,记载 RDB 文件生成后收到的所有写操作。

第三阶段:当主库完结 RDB 文件发送后,就会把此刻 replication buffer 中的修正操作发给从库,从库再从头履行这些操作。

主从仿制整个进程示意图如下所示。

Redis高可用全景一览

这样一来,主从库就完成同步了。不行忽视的是,这个进程中存在着危险点,最常见的便是网络断连或堵塞。假如网络断连,主从库之间就无法进行指令传达了,从库的数据天然也就没办法和主库保持共同了,客户端就或许从从库读到旧数据。

1.2 主从库间网络断了怎么办?

在 Redis 2.8 之前,假如主从库在指令传达时呈现了网络闪断,那么,从库就会和主库从头进行一次全量仿制,开支十分大。从 Redis 2.8 开端,网络断了之后,主从库会选用增量仿制的办法持续同步。只把主从库网络断连期间主库收到的指令,同步给从库。

当主从库断连后,主库会把断连期间收到的写操作指令,写入 replication buffer,一起也会把这些操作指令写入 repl_backlog_buffer 这个缓冲区。repl_backlog_buffer 是一个环形缓冲区,主库会记载自己写到的方位,从库则会记载自己已经读到的方位。

在网络断连阶段,主库或许会收到新的写操作指令,所以,一般来说,主库写到的方位会大于从库读到的方位。当网络康复后,主库只用把主库写到的方位和从库读到的方位之间的指令操作同步给从库就行。

Redis高可用全景一览

Redis 经过主从库方式,既提高了体系处理恳求的吞吐量,也确保了体系的可用性。

假如从库产生毛病了,客户端能够持续向主库或其他从库发送恳求,进行相关的操作。可是假如主库产生毛病了怎么办?此刻从库没有相应的主库能够进行数据仿制操作了,且一旦有写操作恳求,体系也将无法处理。

二、岗兵机制

所以,假如主库挂了,咱们就需要运转一个新主库,比如说把一个从库切换为主库,把它当成主库。在 Redis 主从集群中,岗兵机制是完成主从库主动切换的关键机制。

Redis高可用全景一览

2.1 岗兵的责任

岗兵其实便是一个运转在特殊方式下的 Redis 进程,主从库实例运转的一起,它也在运转。岗兵主要担任的便是三个使命:监控、选主(挑选主库)和告诉。

Redis高可用全景一览

监控是指岗兵进程在运转时,周期性地给所有的主从库发送 PING 指令,检测它们是否依然在线运转。假如从库没有在规定时间内呼应岗兵的 PING 指令,岗兵就会把它符号为“下线状况”;同样,假如主库也没有在规定时间内呼应岗兵的 PING 指令,岗兵就会断定主库下线,然后开端主动切换主库的流程。

选主是指主库挂了今后,岗兵就需要从很多个从库里,依照一定的规矩挑选一个从库实例,把它作为新的主库。这一步完结后,现在的集群里就有了新主库。

然后,岗兵会履行终究一个使命:告诉。在履行告诉使命时,岗兵会把新主库的连接信息发给其他从库,让它们履行 replicaof 指令,和新主库树立连接,并进行数据仿制。一起,岗兵会把新主库的连接信息告诉给客户端,让它们把恳求操作发到新主库上。

可是你有没有想过,假如有岗兵实例在运转时产生了毛病,主从库还能正常切换吗?

2.2 岗兵的高可用

岗兵单点毛病问题,Redis 也是经过树立岗兵集群来处理的。那咱们再回头看岗兵的责任,在监控主从库是否下线时,假如呈现了岗兵内部的意见不统一怎么办?比如说有 3 个岗兵,其中一个岗兵以为主库下线了,而另外 2 个却以为主库是正常的,这时该听谁的呢?

这就比如咱们团队内部呈现了意见分歧,那最好的处理办法便是民主投票了,选用“少数服从多数的准则”。岗兵集群内部也一样,在网络拥塞的情况下,有单个岗兵与主库的 PING 指令失败,这时岗兵就以为该主库毛病了,但是实践并没有。这时就要选用“民主”的办法,大多数岗兵以为主库毛病,才会进行下一步的选主。

岗兵的实例数应该是 2N+1 的奇数,这样才不致于呈现观念敌对的情况,一般咱们至少会装备 3 个岗兵实例。

那选主同样需要考虑一个问题:岗兵这么多,该由哪个履行主从切换?

此刻,这个岗兵就能够再给其他岗兵发送指令,表明期望由自己来履行主从切换,并让所有其他岗兵进行投票。这个投票进程称为“Leader 推举”。由于终究履行主从切换的岗兵称为 Leader,投票进程便是确定 Leader。

到这儿,咱们就大致理清了 Redis 确保服务少中止所采取的一系列方案了。那 Redis 是怎么确保数据少丢掉的呢?

三、AOF和RDB

了解 MySQL 的同学或许听说过,MySQL 是具有 Crash-Safe 的才能的,这归功于数据库的写前日志(Write Ahead Log, WAL) Redo Log。同样,Redis 也供给了 AOF 日志。

3.1 主库宕机了,怎么防止数据丢掉?

AOF 里记载的是 Redis 收到的每一条指令,这些指令是以文本方式保存的。咱们以 Redis 收到“set testkey testvalue”指令后记载的日志为例,看看 AOF 日志的内容。其中,“*3”表明当时指令有三个部分,每部分都是由“$+数字”最初,后边紧跟着详细的指令、键或值。这儿,“数字”表明这部分中的指令、键或值一共有多少字节。例如,“$3 set”表明这部分有 3 个字节,也便是“set”指令。

Redis高可用全景一览

可是,由于记载的是操作指令,而不是实践的数据,所以,用 AOF 办法进行毛病康复的时分,需要逐个把操作日志都履行一遍。假如操作日志十分多,Redis 就会康复得很缓慢,影响到正常运用。

因而,Redis 供给了另一种数据耐久化办法:内存快照 RDB。和 AOF 相比,RDB 记载的是某一时间的数据,并不是操作,所以,在做数据康复时,咱们能够直接把 RDB 文件读入内存,很快地完结康复。

关于快照来说,体系多久履行一次快照直接影响数据丢掉的多少。如下图所示,咱们先在 T0 时间做了一次快照,然后又在 T0+t 时间做了一次快照,在这期间,数据块 5 和 9 被修正了。假如在 t 这段时间内,机器宕机了,那么,只能依照 T0 时间的快照进行康复。此刻,数据块 5 和 9 的修正值由于没有快照记载,就无法康复了。

Redis高可用全景一览

所以,要想尽或许康复数据,t 值就要尽或许小。那么,t 值能够小到什么程度呢,比如说是不是能够每秒做一次快照?

3.2 宕机后,怎么快速康复数据?

Redis 4.0 中提出了一个混合运用 AOF 和 RDB 的办法。简略来说,内存快照以一定的频率履行,在两次快照之间,运用 AOF 日志记载这期间的所有指令操作。

这样一来,快照不用很频频地履行。而且,AOF 日志也只用记载两次快照间的操作,也便是说,不需要记载所有操作了,因而,就不会呈现文件过大的情况了,也能够防止重写开支。

Redis高可用全景一览

这个办法既能享受到 RDB 文件快速康复的优点,又能享受到 AOF 只记载操作指令的简略优势。

小结

我来总结一下本文的内容。Redis 体系的高可用,详细能够经过两个方面来了解:一是服务少中止,二是数据少丢掉。我收拾的常识消化链路如下。

服务少中止 -> 多副本 -> 主从库方式确保数据共同及从库的高可用 -> 岗兵确保主库的高可用 -> 岗兵集群确保岗兵高可用。
数据少丢掉 -> AOF日志 -> AOF康复数据较慢 -> RDB内存快照 -> 履行快照间隔不宜过短 -> AOF+RDB

关于岗兵机制的更多完成细节,我会在后边的内容里持续更新,敬请重视。公众号「杨同学technotes」,欢迎技能交流。