怎么实现高可用呢? 最重要的一点就是冗余数据啊,redis 是通过主从复制来实现数据的冗余存储,这样在主redis down调用之后,切换到从就可以了,这样就实现了故障转移,保证了高可用了,今天我们主要来讲主从复制,至于主down掉之后,怎么切换到从,我们会在下篇再聊。
我想在再看redis 主从复制之前,有必要看下下面这三个基础概念。
备份分为冷备和热备,如果再深入一些还有多活。
注意:这里直接说明了定义,至于出现故障后,怎么做手动或者自动的故障转移,本篇这不讲解,后面讲redis 哨兵的时候,会细讲。上面的图只是能让我们加深对备份概念的理解。
那么redis属于哪种备份呢? 相信你读完肯定就会明白了。
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。一个主节点可以有多个从节点,但一个从节点只能有一个主节点。
看图:
这里我们不会讲或者讲解redis 主从的安装步骤,这个网上的博文和官网都会有,相信大家都会一步步地配置成功。
我们主要会讲解:
当我们配置好主从同步的时候,由于之前没有进行过任何同步,所以首先会进行一次全量数据同步到从库。
主从建立连接
当设置完主从同步配置后,第一步就是主从之间要建立连接,主要之间要相互认识一下,建立信任,才能开始进行同步。
执行slaveof 后,发生了什么,看图说话:
此时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 接收到这两个参数后会保存起来。 看图说话:
发送rdb文件
Master接着就会执行bgsave 生成子进程,完成rdb文件的生成,生成完rdb文件后,会发送rdb文件给Slave,Slave会接收rdb文件,在进行接收之前,会先清空Slave自己的数据库数据【这个过程是阻塞的】,清空完成后,开始接收rdb文件,接收完成之后,就加载rdb文件到内存中。
这里还有一个问题,就是在接收rdb文件的时候,Master可能会有新的写操作过来,由于rdb是某一时刻的内存快照,所以之后的数据,是无法这里进行传输的,这里redis采用了一个缓冲区来解决,在发送rdb开始,新的写请求数据都会放到这个缓冲区一份,等待rdb传输完成之后,Master接着就会传输这个缓冲区的数据到Slave,Slave开始接收,接收完成,主从数据保持一致了,看图说话:
rdb文件的传输:
缓冲区的传输:
后续命令的传输:
其实这个缓冲区的在redis中叫做replication buffer ,redis 会为每一个连接Master的Slave生成一个这样的缓冲区,因为每个Slave开始同步的时刻,可能是不一样的,那同步的进度肯定就不一样了,所以要分别设置一个replication buffer。
只要一个Slave和Master 建立好连接,对应的Slave缓冲区就会建立,如果断开连接,那么这个缓冲区就会释放。
在开始执行bgsave 生成rdb,后续的所有写请求都会保存到这个缓冲区,也就是replication buffer中,也就是后面所有的写请求都会通过这个缓冲区发送给Slave。
看图说话:
如上图所示,每个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(环形缓冲区)的位置图,加深印象:
注意连接没有断开的时候,这两个缓冲区是同时存在,如果连接断开,那么对应Slave的replication buffer缓冲区就会被删除。
其实就是环形的每段记录着当前数据和偏移量,随着当前写入的offset不断增大,因为这是一个环形的缓冲区,就会发生覆盖之前的数据。
环形缓冲区,repl_backlog_buffer 记录是当前Master 接收到新写请求的累计的offset值(master_repl_offset),表示是Master的进度,当发生网络中断时候,所有的Slave都会和Master的offset进行比较,所以它是所有Slave公用的。
增量复制的过程
可以进行增量复制,看图:
如果接收到的runID 和Master runID 相同,同时repl_backlog_buffer缓冲区的offset会与Slave 发过来的offset进行比较,如果主从节点的差距没有超过环形缓冲区的长度,或者没有发生套圈,也就是不会发生覆盖之前的数据,那么Master会回复Continue给Slave,告诉Slave可以进行增量复制了。
如果发现runID和现在Master不一致,或者 主从的offset差距超过的repl_backlog_buffer缓冲区的长度,那么就会走全量复制了,这里就不多说了。
由于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 是在从库和主库建立连接成功后创建的,在主从断开后,这个缓冲区也会被主库进行删除,主从库之间复制命令的传输,都会经过这个buffer,而且这个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,如果从再来请求链接,可能会造成恶性循环。
今天的分享就到这里了,码字画图不易,期待你的点赞、关注、转发,谢谢。