Redis基础-主从库模式

Redis通过AOF和RDB的方式恢复数据,保证尽量少丢失数据,提升可靠性。通过增加副本冗余量,将一份数据同时保存在多个实例上,保证一个实例出现故障,其他实例也可以对外提供服务。

主从库模式

为了保证多实例之间数据一致性,Redis提供了主从库模式,以保证数据副本的一致性,主从库之间采用的是读写分离的方式。

读操作:主库、从库都可以接收;

写操作:首先到主库执行,然后,主库将写操作同步给从库。

Redis基础-主从库模式

 

 

 主从库数据同步

当我们启动多个 Redis 实例的时候,它们相互之间就可以通过 replicaof命令形成主库和从库的关系。

  例:在从库上执行 replicaof 127.0.0.1 6379 之后,从库就从主库上复制数据了

主从库数据第一次同步:

Redis基础-主从库模式

 

第一阶段是主从库间建立连接、协商同步的过程,主要是为全量复制做准备。在这一步,从库和主库建立起连接,并告诉主库即将进行同步,主库确认回复后,主从库间就可以开始同步了。

具体来说,从库给主库发送 psync 命令,表示要进行数据同步,主库根据这个命令的参数来启动复制。psync 命令包含了主库的 runID 和复制进度 offset 两个参数。

  runID,是每个 Redis 实例启动时都会自动生成的一个随机 ID,用来唯一标记这个实例。当从库和主库第一次复制时,因为不知道主库的 runID,所以将 runID 设为“?”。

  offset,此时设为 -1,表示第一次复制。

主库收到 psync 命令后,会用 FULLRESYNC 响应命令带上两个参数:主库 runID 和主库目前的复制进度 offset,返回给从库。

从库收到响应后,会记录下这两个参数。这里有个地方需要注意,FULLRESYNC 响应表示第一次复制采用的全量复制,也就是说,主库会把当前所有的数据都复制给从库。

在第二阶段,主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载。这个过程依赖于内存快照生成的 RDB 文件。

第三个阶段,主库会把第二阶段执行过程中新收到的写命令,再发送给从库。

主从级联模式

 

 如果从库数量很多,而且都要和主库进行全量复制的话,就会导致主库忙于 fork 子进程生成 RDB 文件,进行数据全量同步。fork 这个操作会阻塞主线程处理正常请求,从而导致主库响应应用程序的请求速度变慢。此外,传输 RDB 文件也会占用主库的网络带宽,同样会给主库的资源使用带来压力。

“主 - 从 - 从”模式

简单来说,我们在部署主从集群的时候,可以手动选择一个从库(比如选择内存资源配置较高的从库),用于级联其他的从库。然后,我们可以再选择一些从库(例如三分之一的从库),在这些从库上执行如下命令,让它们和刚才所选的从库,建立起主从关系。

Redis基础-主从库模式

 

 

 一旦主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销。

repl_backlog_buffer

repl_backlog_buffer 是一个环形缓冲区,主库会记录自己写到的位置,从库则会记录自己已经读到的位置。

刚开始的时候,主库和从库的写读位置在一起,这算是它们的起始位置。随着主库不断接收新的写操作,它在缓冲区中的写位置会逐步偏离起始位置,我们通常用偏移量来衡量这个偏移距离的大小,对主库来说,对应的偏移量就是 master_repl_offset。主库接收的新写操作越多,这个值就会越大。同样,从库在复制完写操作命令后,它在缓冲区中的读位置也开始逐步偏移刚才的起始位置,此时,从库已复制的偏移量 slave_repl_offset 也在不断增加。正常情况下,这两个偏移量基本相等。

Redis基础-主从库模式

 

在网络断连阶段,主库可能会收到新的写操作命令,所以,一般来说,master_repl_offset 会大于 slave_repl_offset。此时,主库只用把 master_repl_offset 和 slave_repl_offset 之间的命令操作同步给从库就行。

 

 Redis基础-主从库模式

 

repl_backlog_buffer是所有从库共享的,slave_repl_offset是由从库自己记录的,这也是因为每个从库的复制进度不一定相同。 从库断连后再恢复时,会给主库发送psync命令,并把自己当前的slave_repl_offset发给主库。所以,在你所说的情况下,正常恢复的从库推进的slave_repl_offset是它自己记录的,不会影响到第二个从库。 第二个从库断连后再恢复,只要slave_repl_offset指向的数据没有被覆盖,就能继续恢复。

repl_backlog_size

为了避免从库的读取速度比较慢,导致从库还未读取的操作被主库新写的操作覆盖,可以将repl_backlog_size这个参数调整大一些,降低主从库间的数据不一致的风险。

 

 

1、repl_backlog_buffer:就是上面我解释到的,它是为了从库断开之后,如何找到主从差异数据而设计的环形缓冲区,从而避免全量同步带来的性能开销。如果从库断开时间太久,repl_backlog_buffer环形缓冲区被主库的写命令覆盖了,那么从库连上主库后只能乖乖地进行一次全量同步,所以repl_backlog_buffer配置尽量大一些,可以降低主从断开后全量同步的概率。而在repl_backlog_buffer中找主从差异的数据后,如何发给从库呢?这就用到了replication buffer。

2、replication buffer:Redis和客户端通信也好,和从库通信也好,Redis都需要给分配一个 内存buffer进行数据交互,客户端是一个client,从库也是一个client,我们每个client连上Redis后,Redis都会分配一个client buffer,所有数据交互都是通过这个buffer进行的:Redis先把数据写到这个buffer中,然后再把buffer中的数据发到client socket中再通过网络发送出去,这样就完成了数据交互。所以主从在增量同步时,从库作为一个client,也会分配一个buffer,只不过这个buffer专门用来传播用户的写命令到从库,保证主从数据一致,我们通常把它叫做replication buffer。

3、再延伸一下,既然有这个内存buffer存在,那么这个buffer有没有限制呢?如果主从在传播命令时,因为某些原因从库处理得非常慢,那么主库上的这个buffer就会持续增长,消耗大量的内存资源,甚至OOM。所以Redis提供了client-output-buffer-limit参数限制这个buffer的大小,如果超过限制,主库会强制断开这个client的连接,也就是说从库处理慢导致主库内存buffer的积压达到限制后,主库会强制断开从库的连接,此时主从复制会中断,中断后如果从库再次发起复制请求,那么此时可能会导致恶性循环,引发复制风暴,这种情况需要格外注意

 

上一篇:redis主从复制


下一篇:双主复制配置