07-Redis【Redis主从复制,终于有人讲明白了】

2,779 阅读12分钟

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

前言

大家好,我是飓风

往期文章索引如下:

00-Redis 你真的了解吗?
01-Redis 数据类型你知道的不止这些
02-Redis 哈希表的门道
03-Redis 凭什么这么快
04-Redis 持久化AOF你真的了解吗?
05-Redis 持久化之RDB 的奥秘
06-Redis 高频面试题 缓存的【雪崩-击穿-穿透】不为人知的秘密

前面的 0405 我们探讨了redis 的持久化,虽然redis 在down机之后,能够靠持久化机制,来恢复数据,之后就可以进行正常的请求了,当时从down掉到恢复这段时间里,服务是不可用的,那么redis是怎么实现高可用的故障转移呢?

那怎么实现高可用呢? 最重要的一点就是冗余数据啊,redis 是通过主从复制来实现数据的冗余存储,这样在主redis down调用之后,切换到从就可以了,这样就实现了故障转移,保证了高可用了,今天我们主要来讲主从复制,至于主down掉之后,怎么切换到从,我们会在下篇再聊。

怎么做备份

我想在再看redis 主从复制之前,有必要看下下面这三个基础概念。

备份分为冷备热备,如果再深入一些还有多活

  • 热备:由主库或者说是主数据中心承担业务流量,同时会实时的备份数据到从库或者从的数据中心。如下图。

image.png

  • 冷备:由主库或者主数据中心承担业务流量,备份数据会定时或者离线手动的执行脚本同步数据到一个从库或者从数据中心,如下图。

image.png

  • 双活:由两个数据中心承担业务流量,数据中心互为主备,一般主数据中心会承担大部分流量,另一数据中心会承担小部分流量,如下图。

image.png

注意:这里直说说明了定义,至于出现故障后,怎么做手动或者自动的故障转移,本篇这不讲解,后面讲redis 哨兵的时候,会细讲。上面的图只是能让我们加深对备份概念的理解。

那么redis属于哪种备份呢? 相信你读完肯定就会明白了。

定义

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。一个主节点可以有多个从节点,但一个从节点只能有一个主节点。

看图:

image.png

这里我们不会讲或者讲解redis 主从的安装步骤,这个网上的博文和官网都会有,相信大家都会一步步的配置成功。

我们主要会讲解:

  • 主从复制从开始到现在都经历了什么,同步的是什么,数据还是文件?
  • 在主从过程中,如果网络中断了,怎么办?
  • 同步的过程中,哪些因素会增加主库的压力?

全量同步

当我们配置好主从同步的时候,由于之前没有进行过任何同步,所以首先会进行一次全量数据同步到从库。

主从建立连接

当设置完主从同步配置后,第一步就是主从之间要建立连接,主要之间要相互认识一下,建立信任,才能开始进行同步。

执行slaveof 后,发生了什么,看图说话:

image.png

此时Slave从库会主动和Master主库进行通信,发送psync 命令,该命令会捎带两个参数过去给Master,第一个参数是主库ID(runID),redis 在启动的时候,都会为自己生成一个ID,第二个是Slave 需要从Master哪里开始复制数据,也就是Slave复制Maser数据的偏移量offset。

Slave第一次和Master进行通信,由于一开始不知道Master的ID,所以传递了?
由于是第一次复制,传递-1 表示第一次要进行全量复制。

接着Master接收到了Slave传递过来的命令以及相应的参数,一看是? 和 -1 ,那么就知道这个Slave要进行全量的复制,Master会给Slave 发送一个fullresync 命令,告诉Slave接下来要开始全量复制,并带上自己的ID,Slave 接收到这两个参数后会保存起来。 看图说话:

image.png

发送rdb文件

Master接着就会执行bgsave 生成子进程,完成rdb文件的生成,生成完rdb文件后,会发送rdb文件给Slave,Slave会接收rdb文件,在进行接收之前,会先清空Slave自己的数据库数据【这个过程是阻塞的】,清空完成后,开始接收rdb文件,接收完成之后,就加载rdb文件到内存中。

这里还有一个问题,就是在接收rdb文件的时候,Master可能会有新的写操作过来,由于rdb是某一时刻的内存快照,所以之后的数据,是无法这里进行传输的,这里redis采用了一个缓冲区来解决,在发送rdb开始,新的写请求数据都会放到这个缓冲区一份,等待rdb传输完成之后,Master接着就会传输这个缓冲区的数据到Slave,Slave开始接收,接收完成,主从数据保持一致了,看图说话:

rdb文件的传输:

image.png

缓冲区的传输:

image.png

后续命令的传输:

其实这个缓冲区的在redis中叫做replication buffer ,redis 会为每一个连接Master的Slave生成一个这样的缓冲区,因为每个Slave开始同步的时刻,可能是不一样的,那同步的进度肯定就不一样了,所以要分别设置一个replication buffer。

只要一个Slave和Master 建立好连接,对应的Slave缓冲区就会建立,如果断开连接,那么这个缓冲区就会释放。

在开始执行bgsave 生成rdb,后续的所有写请求都会保存到这个缓冲区,也就是replication buffer中,也就是后面所有的写请求都会通过这个缓冲区发送给Slave。

看图说话:

image.png

如上图所示,每个Slave对应一个缓冲区,也就是replication buffer。

增量复制

其实上面整个过程完成之后,全量复制就完成了,只要连接不中断,那么会持续进行主从的复制,那么你有没有想过,如果网关抖动了或者中断了,主从连接断开了,redis 会怎么处理呢?重新走全量复制吗?

网络中断了,怎么办?

如果网络发生中断,在redis2.8之前会再走一次全量生成rdb进行复制传输的,这个是很耗费资源和性能的操作。redis2.8以后,对这个过程做了优化,采用增量复制的机制,来减少数据的传出,达到了快速复制的目的,下面主要来讲解增量复制的过程。

还记得全量复制的时候,会返回给Slave一个偏移量吗?其实Slave在接收数据之后,会增加这个偏移量来记录当前接收Master多少数据了。如果Master和Slave的偏移量是1000 ,传递30字节给Slave,那么此时Master和Slave的偏移量应该是1030.

如果网络发生了中断,就会重试和Master重新连接,连接之后,会发送自己的offset给Master,Master会根据Slave发送的偏移量来决定是给Slave做增量复制还是做全量复制。

知道了大概的过程,那么在网络中断之后,恢复连接之前,中断这段时间内的数据,肯定是同步不过去了,那么数据存储在哪里了呢?

只要开始进行主从复制了,那么新的写请求在写入replication buffer的同时,也都会写入到一个叫做repl_backlog_buffer 的缓冲区内,这是一个环形缓冲区,会记录Master接收新的写请求数据的偏移量和新写命令,这样Slave再重新连接之后,就可以从这里接着发送命令给Slave了。

看下replication buffer 和 repl_backlog_buffer(环形缓冲区)的位置图,加深印象:

image.png

注意连接没有断开的时候,这两个缓冲区是同时存在,如果连接断开,那么对应Slave的replication buffer缓冲区就会被删除

其实就是环形的每段记录着当前数据和偏移量,随着当前写入的offset不断增大,因为这是一个环形的缓冲区,就会发生覆盖之前的数据

环形缓冲区,repl_backlog_buffer 记录是当前Master 接收到新写请求的累计的offset值(master_repl_offset),表示是Master的进度,当发生网络中断时候,所有的Slave都会和Master的offset进行比较,所以它是所有Slave公用的。

增量复制的过程

  1. Slave尝试发送psync 带上Master的runID和 自己的 offset (slave_repl_offset) 。

image.png

  1. Master接收到psync 之后,进行判断是进行增量复制还是进行全量复制。

可以进行增量复制,看图:

image.png

如果接收到的runID 和Master runID 相同,同时repl_backlog_buffer缓冲区的offset会与Slave 发过来的offset进行比较,如果主从节点的差距没有超过环形缓冲区的长度,或者没有发生套圈,也就是不会发生覆盖之前的数据,那么Master会回复Continue给Slave,告诉Slave可以进行增量复制了。

如果发现runID和现在Master不一致,或者 主从的offset差距超过的repl_backlog_buffer缓冲区的长度,那么就会走全量复制了,这里就不多说了。

  1. Master发送offset之后的命令给Slave,看图说话。

image.png

由于Slave发送的过来的offset是998 ,现在Master的offset是1000,所以Master会把998-1000之间的命令继续传递给Slave,这样就做到增量传输了。

总结

到现在整个redis 主从复制的过程就讲解完成了,现在来做下总结。

主从同步分为两个类型:

  • 全量同步

全量同步redis 会执行bgsave 来生成rdb文件,然后发送给从库,从库接收之前会先清空从库的数据空,防止之前有数据造成数据的污染,接收完rdb文件之后,就会就加载rdb文件到内存,这是同步其实并没有完成,在进行生成rdb文件的时候,还会有新的写请求过来,此时这些写请求会缓存在一个缓冲区内,这个缓冲区叫做replication buffer,当从库加载完rdb之后,就会接收这个缓冲区的所有写命令了,到此全量复制就结束了。

由于生产rdb是会阻塞主线程,这个过程很耗费资源,如果采用一个主多个从的方式,那么势必会增加主库的压力,可以选择一个从,再从库上再分裂出一个从或者多个从,来减少主库的压力。

如果想要快速的生成rdb文件 ,应该减少redis设置内存的大小,这样生成rdb文件就会很快,减少阻塞的时间。

  • 增量同步

如果主从断开连接了,redis 主库会判断是进行全量复制还是增量复制,主库会根据从库发送过来的runID和从库复制进行offset,如果runID和主库的ID相同,并且主从的offset差距没有超过repl_backlog_buffer缓冲区的长度,就会复制offset之间的repl_backlog_buffer的命令给Slave。

两个缓冲区:

  • replication buffer

replication buffer 是在从库和主库建立连接成功后创建的,在主从断开后,这个缓冲区也会被主库进行删除,主从库之间复制命令的传输,都会经过这个buffer,而且这个buffer是每个从库独有的。

  • repl_backlog_buffer

开始进行命令传输之前,就会建立好这个buffer,这个buffer记录当前Master接收到的新的写操作命令offset和命令本身,是所有Slave公用的buffer,Slave 发送psync之后,会和Master的offset进行比较,来决定是否进行增量复制。

注意点:

1、redis 实例的内存大小不要设置太大,这样能够缩短生成rdb文件的时间,同时也能缩短全量复制的时间,减少带宽的占用。
2、如果从库和主库断开连接超时很长,那么repl_backlog_buffer缓冲区内的数据很可能就会被覆盖了,进而会退化为全量复制了,此时可以设置repl_backlog_size 这个参数设置大些。
3、replication buffer,这个缓冲区也要留意,如果从库接收的很慢,这个缓冲区会满,redis可能就会OOM了,如果这个buffer满了redis 会怎么处理,redis提供了client-output-buffer-limit参数限制这个buffer的大小,如果满了,主库会和从断开连接,删除buffer,如果从再来请求链接,可能会造成恶性循环。


今天的分享就到这里了,码字画图不易,期待你的点赞、关注、转发,谢谢。

你的点赞、关注 是飓风创作的最大动力。

如有问题 欢迎人才请留言,一起讨论和勘误。

欢迎关注 github

微信添加: zookeeper0