redis学习笔记(四):redis的主从复制

目录

一、背景介绍

二、主从复制的架构

三、主从复制的原理

1、全量复制

2、增量复制

3、同源增量同步

4、无磁盘化复制

5、共享主从复制缓冲区

 6、Forkless全量同步

四、总结

1、主从复制的特点

2、主从复制的优点

3、主从复制的缺点

4、其他


一、背景介绍

        Redis具有高可靠性,主要体现在两方面:一是数据尽量少丢失,二是服务尽量少中断。Redis的AOF和RDB保证了前者;而对于后者,redis通过增加副本冗余,将一份数据同时保存在多份实例上。这样的话,当单个服务出现单点故障的问题,就可以把数据复制多个副本放在不同的服务器上,且这些拥有数据副本的服务器可以处理客户端的读请求,不会影响到业务的使用。

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

  • 读操作:主库、从库都可以接收。
  • 写操作:首先到主库中执行,然后主库将写操作同步给从库

二、主从复制的架构

        主从复制将数据库分为主节点和从节点,主节点源源不断地将数据复制给从节点,保证主从节点中存有相同的数据。有了主从复制,数据可以有多份副本,这样既可以提升数据库系统的请求处理能力和系统的可用性。

        Redis Replication是一种 master-slave 模式的复制机制,这种机制使得 slave 节点可以成为与 master 节点完全相同的副本,可以采用一主多从或者级联结构。

 主从复制的配置要点:

  • 配从库不配主,从库配置:salveof 主库ip 主库端口
  • 查看redis的配置信息:info replication

        通过slaveof <host> <port>命令,或者通过配置slaveof选项,配置当前的服务器(slave)复制指定服务器(master)的内容,被复制的服务器称为主服务器(master),对主服务器进行复制操作的为从服务器(slave)

        主服务器master可以进行读写操作,当主服务器的数据发生变化,master会发出命令流来保持对salve的更新,而从服务器slave通常是只读的(可以通过slave-read-only指定),在主从复制模式下,即便master宕机了,slave是不能变为主服务器进行写操作的。

三、主从复制的原理

        Redis主从复制的策略就是:当主从服务器刚建立连接的时候,进行全量同步;全量复制结束后,进行增量复制。如果有需要,slave 在任何时候都可以发起全量同步。

1、全量复制

        全量复制发生于slave的初始化阶段,由主节点产生一份全量数据的快照,即RDB文件,slave将master上的所有数据都复制一份。在产生快照的时刻起,主节点Master会记录新接收到的写命令。当快照发送完毕后,将累积的写命令发送给从节点,从节点执行写命令。全量数据同步完成后,主节点Master将执行过的写命令不断地发送给从节点,从节点执行这些命令,从而保证主从节点中数据的同步变更,进而保证主从节点数据的一致性。

其流程如下:

(1)slave服务器连接到master服务器,发送psync命令(Redis2.8之前是sync命令),请求数据同步;

psync命令包含了主库的runID和复制进度offset两个参数

  • runID:每个redis实例启动时都会自动生成一个随机的runID,用来唯一标记这个实例。当从库和主库第一个复制时,因为不知道主库的runID,所以设置为?。
  • offset,此时为-1,表示第一次复制。
    主库收到psync命令后,会用fullresync 响应命令带上两个参数:主库runID和主库目前的复制进度offset,返回给从库。从库收到响应后,会记录下这两个参数。
    这里有个地方需要注意,fullresync响应表示第一次复制采用的时全量复制,也就是说,主库会把当前所有的数据都复制给从库

(2)master服务器收到psync命令之后,开始执行bgsave命令生成RDB快照文件并使用缓存区记录此后执行的所有写命令;

  • 如果master收到了多个slave并发连接请求,只会进行一次持久化,而不是每个连接都执行一次,然后再把这一份持久化的数据发送给多个并发连接的slave。
  • 如果RDB复制时间超过60秒(repl-timeout),那么slave服务器就会认为复制失败,可以适当调节大这个参数

(3)master服务器bgsave执行完之后,就会向所有Slava服务器发送快照文件,并在发送期间继续在缓冲区内记录被执行的写命令。在主库将数据同步给从库的过程中,主库不会阻塞,仍然可以正常接收请求。否则,redis的服务就被中断了。但是,这些请求中的写操作并没有记录到刚刚生成的RDB文件中。为了保证主从库的数据一致性,主库会在内存中用专门的replication buffer,记录RDB文件生成收到的所有写操作。

client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败

(4)slave服务器收到RDB快照文件后,会将接收到的数据写入磁盘,然后清空所有旧数据,将收到的快照存入本地磁盘的内存中,并基于旧的数据版本对外提供服务;

(5)master服务器发送完RDB快照文件之后,便开始向slave服务器发送缓冲区中的写命令;

(6)slave服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令

(7)如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF。

全量复制带来的问题:

(1)fork带来的问题

        在主节点收到fork请求后,会执行fork创建子进程,由子进程将所有数据存储到RDB中,产生数据库的快照。然而执行fork 时候,需要拷贝大量的内存页表,特别是当内存使用量较大的时候,这是一个耗时较多的操作,尤其当内存使用量较大的时候。在内存占用 10GB 时,fork 需要消耗 100 多毫秒,并且fork 的时候会将主进程阻塞 100 多毫秒。此外,在fork 之后,如果主库中有不少的写入,那么由于写时复制机制,会额外消耗不少的内存,还会增大响应时间。

(2)主从库间网络闪断如何解决

        假如主从库之间的网络出现故障,连接意外断开,主节点无法继续传播命令至该从节点。在网络恢复之后,从节点重新连接上主节点后,主节点无法继续传播新接收到的命令,因为从节点已经漏掉了一些命令。此时,从节点需要从头再来,再次执行全部的同步过程,而这要付出很高的代价。网络闪断期间主节点中可能只写入了比较少的数据,但就因为这很少的一部分数据,需要让从节点进行一次代价高昂的全量同步。这种做法是非常低效的。

2、增量复制

       前面提到了全量复制在网络闪断的时候会使得从节点进行再一次的全量同步,这种做法开销巨大,而增量同步是可以解决这个问题的。在redis2.8之后,主从库之间次啊用增量同步的方法继续同步。Redis的增量复制是指在初始化的全量复制并开始正常工作之后,master服务器将发生的写操作同步到slave服务器的过程,增量复制的过程主要是master服务器每执行一个写命令就会向slave服务器发送相同的写命令,slave服务器接收并执行收到的写命令。

如何解决网络闪断的问题?

        主从库断连后,主库会把断联期间收到的写操作命令写入replication buffer,并将这些操作写入repl_backlog_buffer 这个缓存区。repl_backlog_buffer是一个环形缓冲区,主库会记录自己写到的位置,从库则会记录自己读到的位置。

         主从库的连接恢复后,从库首先会给主库发送psync,并把runID和自己当前的offset(slave_repl_offset)发送给主库,主库会判断自己的offset(master_repl_offset)和slave_repl_offset之间的差距。

  • 注意 如果发送过去的runID 不是此时的主库ID,这就说明可能原来的主库下线,哨兵重新选举了主库,那么此时就需要进行全量复制
  • 增量复制不止是网络断联才用,在进行完第一次全量复制之后,主从之间就是进行增量复制。当从节点发送的runID与主节点一致时,说明之前进行过同步,可以进行增量复制

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

        如上示意图中间部分,主库和从库之间相差了put d e 和 put d f两个操作,因此在增量复制时,主库只需要把他们同步给从库即可。

         需要注意的是repl_backlog_buffer是一个环形缓冲区,所以在缓冲区写满后,主库还会继续写入,此时,就会覆盖掉之前的写入的操作。如果从库的读取速度比较慢,就有可能导致从库还未读取的操作被主库新写的操作覆盖了,这就会导致主从数据不一致。这就可以增大缓冲区解决问题。如果还是会出现出从offset超过缓冲区大小,那么就不能进行增量复制(这样会丢失数据),转而使用全量复制。

增量复制存在的问题:

(1)节点重启

        增量同步依赖主节点的编号和复制偏移量,从节点在初次同步的时候会获取到主节点的编号,并在之后的同步中不断调整复制偏移量,这些信息都存储在内存中。当从节点意外重启后,尽管本地存有 RDB 或 AOF 文件,还是需要进行一次全量同步。但实际上完全可以载入本地数据,并执行部分同步即可。

(2)主从切换

        如果主节点意外宕机,外围监控组件执行了主从切换。此时其他从节点对应的主节点就变化了,从节点中记录的主节点编号就匹配不上新的主节点了,此时会进行一次全量同步。但实际上所有的从节点在主从切换之前同步进度应该是差不多的,而且新提升的从节点包含的数据应该最全,切主后所有从节点都执行一次全量同步,这实在不合理。

3、同源增量同步

        从节点重启后会丢失原主节点编号和复制偏移量,这导致重启后需要全量同步,那么我们只需要把这些信息存下来就可以了。在主从切换后,主节点信息发生变化,导致从节点需要全量同步。那么只需能确认新主节点上的数据是从原主节点复制来的,则可以继续从新的主节点上进行复制。

        在Redis 4.0 以后,对 PSYNC 进行了改进,提出了同源增量复制的解决方案,该方案解决了前面提到的两个问题。

        从节点重启后,需要跟主节点全量同步,其原因在于从节点丢失了主节点的编号信息。而在 Redis 4.0 后,主节点的编号信息被写入到 RDB 中持久化保存。主节点切换后,从节点需要和新主节点全量同步,本质原因是新的主节点不认原主节点的编号。如果从节点发送 PSYNC <原主节点编号> <复制偏移量>,那么新的主节点能够识别到这里主节点的编号其实是原主节点,且自己的数据就是从该节点复制来的。那么新的主节点就应该明白,它和其他从节点数据来源相同,应该接收增量同步。因此只需要让从节点在切换为主节点时,将自己之前的主节点的编号记录下来即可。有了同源增量复制后,主节点切换后,其他从节点可以基于新的主节点继续增量同步。

4、无磁盘化复制

        在Redis2.8之后,可以通过无盘复制来达到目的,由master直接开启一个socket,在内存中创建RDB文件,再将rdb文件发送给slave服务器,不使用磁盘作为中间存储。(无盘复制一般应用在磁盘空间有限但是网络状态良好的情况下)

 repl-diskless-sync :是否开启无磁盘复制
repl-diskless-sync-delay:等待一定时长再开始复制,因为要等更多slave重新连接过来

5、共享主从复制缓冲区

        在主节点的视角中,从节点就是一个客户端,从节点发送了 PSYNC 命令后,主节点就要与它们完成全量同步,并不断地把写命令同步给从节点。Redis 的每个客户端连接上存在一个发送缓冲区。主节点执行了写命令后,就会将命令内容写入到各个连接的发送缓冲区中。发送缓冲区存储的是待传播的命令,这意味着多个发送缓冲区中的内容其实是相同的。而且,这些命令还在复制积压缓冲区中存了一份呢。这就造成了大量的内存浪费,尤其是存在很多从节点的时候。

        Redis 7.0 中提出并实现了一个共享主从复制缓冲区的方案解决了这个问题。该方案让发送缓冲区与复制积压缓冲区共享,避免了数据的重复,可有效节省内存。

 6、Forkless全量同步

        全量同步时需要 fork 一个子进程,但 fork 是一个开销较大的操作。为了避免 fork 影响 Redis 服务的稳定性,forkless 的方案被提出。即,全量同步时不依赖于 fork。

        如果不使用 fork,可以分多次逐步遍历 Redis 的主哈希表,一边遍历一边写 RDB 文件。当有新的写操作修改了遍历过的数据时,将变更记录下来。遍历完成后,将变更追加到 RDB 文件中。如此就可以在不执行 fork 的条件下,生成一份数据的快照。

四、总结

1、主从复制的特点

(1)Redis使用异步复制,每次接收到写命令之后,先在内部写入数据,然后异步发送给slave服务器。但从Redis 2.8开始,从服务器会周期性的应答从复制流中处理的数据量。

(2)Redis主从复制不阻塞master服务器。当若干个从服务器在进行初始同步时,主服务器仍然可以处理外界请求。

(3)主从复制不阻塞slave服务器。当master服务器进行初始同步时,slave服务器返回的是以前旧版本的数据,如果你不想这样,那么在启动redis配置文件中进行设置,那么从redis在同步过程中来自外界的查询请求都会返回错误给客户端;

        虽然说主从复制过程中对于从redis是非阻塞的,它会用旧的数据集来提供服务,但是当初始同步完成后,需删除旧数据集和加载新的数据集,在这个短暂时间内,从服务器会阻塞连接进来的请求,对于大数据集,加载到内存的时间也是比较多的。

(4)主从复制提高了redis服务的扩展性,避免单个redis服务器的读写访问压力过大的问题,同时也可以给为数据备份及冗余提供一种解决方案;

(5)使用主从复制可以为master服务器免除把数据写入磁盘的消耗,可以配置让master服务器不再将数据持久化到磁盘,而是通过连接让一个配置的slave类型的Redis服务器及时将相关数据持久化到磁盘。不过这种做法存在master类型的Redis服务器一旦重启,因为此时master服务器不进行持久化,所以数据为空,这时候通过主从同步可能导致slave类型的Redis服务器上的数据也被清空,所以这个配置要确保主服务器不会自动重启(详见第2点的“master开启持久化对主从架构的安全意义”)

2、主从复制的优点

(1)数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

(2)故障恢复:如果master宕掉了,使用哨兵模式,可以提升一个 slave 作为新的 master,进而实现故障转移,实现高可用

(3)负载均衡:可以轻易地实现横向扩展,实现读写分离,一个 master 用于写,多个 slave 用于分摊读的压力,从而实现高并发;

3、主从复制的缺点

        由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave服务器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。

4、其他

(1) Redis 的主从复制分为全量同步和命令传播两个阶段。主节点先发送快照给从节点,然后源源不断地将命令传播给从节点,以此保证主从数据的一致。

(2)针对Redis 2.8 之前的主从复制在网络闪断后需要重新全量同步的问题,Redis 2.8 引入了复制积压缓冲区。

(3)Redis 4.0 中,同源增量复制的策略解决了主从切换后从节点需要全量同步的问题

(4)Redis 6.0 中,为进一步优化主从复制的性能,无盘同步和加载的提出避免全量同步时读写磁盘,提高主从同步的速度。

(5)Redis 7.0 中采用共享主从复制缓冲区的策略,降低了主从复制带来的内存开销。

(6)为了避免调用 fork 时对主进程的造成长时间的阻塞,forkless 方案在不依赖 fork 的情况下完成全量同步,降低主从复制时对业务的影响。
 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值