Redis(九):Redis的主从同步

1. 主从同步

    1.1 初次全同步

    1.2 命令传播

2. 断线后重复制

    2.1 旧版重复制功能

    2.2 新版重复制功能

3. 主从同步的一致性分析


    redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制另一个服务器,我们称呼被复制的服务器为主服务器(master),而对主服务器进行复制的服务器称为从服务器(slave)。假设有两个redis服务器,地址分别为127.0.0.1:6379和127.0.0.1:12345,如果我们向服务器127.0.0.1:12345发送以下命令:

127.0.0.1:12345> SALVEOF 127.0.0.1 6379

    那么服务器127.0.0.1:12345将会成为127.0.0.1:6379的从服务器,而服务器127.0.0.1:6379则会成为127.0.0.1:12345的主服务器。进行复制的主从服务器双方的数据库将保存相同的数据,概念上将这种现象称为“数据库状态一致”。 

    redis的复制功能分为同步和命令传播两个操作,同步操作用于将从服务器的数据库状态更新至主服务器所处的数据库状态。命令传播操作则用于主服务器的数据库状态被修改时,导致主从服务器的数据库状态出现不一致时,让主从服务器的数据库重新回到一致状态。

1. 主从同步

    redis的主从同步分为初次全同步和命令传播两个操作,初次全同步操作将从服务器的状态更新至主服务器的状态,而命令传播则用于在主服务器的数据库状态被更改时,需要将主服务器的写命令传播给从服务器。

1.1 初次全同步

      从服务器对主服务器的初次全同步操作需要通过向主服务器发送SYNC命令来完成,以下是主服务器(master)收到SYNC命令后的执行步骤:

         

    从服务器的几种状态(slave.replstate)介绍:

  • REDIS_REPL_WAIT_BGSAVE_START:该从服务器需要生成一个新的RDB文件,这种情况发生在SYNC命令执行时,正在有BGSAVE正在执行,但是没有任何slave在等这个BGSAVE,因此需要在当前BGSAVE命令执行完成之后开始一个新的BGSAVE;

  • REDIS_REPL_WAIT_BGSAVE_END:该从服务器正在等待RDB文件的生成,这种情况发生在SYNC命令执行时,刚好正在执行的BGSAVE是这个slave所等待的;

  • REDIS_REPL_SEND_BULK:该从服务器正在等待主服务器发送RDB文件给它;

  • REDIS_REPL_ONLINE:该从服务器的RDB文件已经接收完成,进入上线状态,接下来只发送命令的传播;

    初次全同步流程的整个过程如下:

(1)从服务器向主服务器发送SYNC命令;

(2)主服务器收到SYNC命令后,创建一个slave节点,加到server.slaves链表中,主服务器根据需要是否执行BGSAVE命令:

  • 如果刚好有BGSAVE命令正在执行,则判断是否有slave也在等待这个BGSAVE的完成,如果有则该slave和新生成和slave可以使用同一个RDB文件和相同的回复缓冲区;
  • 如果刚好有BGSAVE命令正在执行,但是没有任何slave等待这个BGSAVE的完成,那么这个新生成的slave需要等下一个BGSAVE的执行;
  • 如果没有BGSAVE在执行,那么fork一个子进程,为这个slave生成一个RDB;

(3)BGSAVE命令执行完毕,父进程收到子进程发来的信号后:

  • 遍历所有slave,判断是否还有slave需要新生成一个RDB,如果有则再次fork一个子进程,生成一个新的RDB
  • 遍历所有slave,判断是否有slave在等待这次的BGSAVE,如果有,那么为slave注册写事件处理器,在写事件回调函数sendBulkToSlave中,主服务器将RDB文件发送给从服务器。写完RDB文件之后,再创建向slave发送命令的写事件处理器,将保存并发送 RDB 期间的命令全部发送给从服务器。从服务器接收这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态;

(4)主服务器在RDB文件写完后会注册发送命令的写事件处理器,主服务器监控到该slave的socket fd可写时,将记录在slave的回复缓冲区的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态;

1.2 命令传播

    SLAVEOF命令执行完毕之后,此时master和slave服务器的数据库达到一致,接下来master每收到客户端的一条写指令时,都会将这条命令传播给所有的slave服务器,这就是命令传播功能,主要通过replicationFeedSlaves()函数实现,该函数的流程如下:

        

(1)本次命令的db可能改变,如果server.slaveseldb和本次修改的db不同的话,首先需要向所有slave发送SELECT命令,改变slave的db;

(2)接着把命令写入复制积压缓冲区server.backlog并且更新复制偏移量server.master_repl_offset,缓冲区和偏移量主要用于断线后重复制时使用,在下一节中介绍

(3)最后需要调用addReply函数,将本次的命令发送到各个slave服务器,具体来说就是把命令复制到各个slave的回复缓冲区,然后注册写回调函数,加入到epoll的监听之中。下一轮事件循环当slave的socket可写时,就会将这些命令写入到所有slave中去。

2. 断线后重复制

    断线后重复制主要针对处于命令传播阶段的主从服务器因为网络原因而中断了复制,但是从服务器通过自动重连接重新连上了主服务器,并继续复制主服务器。

2.1 旧版重复制功能

    Redis2.8以前从服务器对主服务器的复制可以分为两种情况:

  • 初次复制:从服务器以前从没有复制过任何服务器,这是第一次复制
  • 断线后复制:处于命令传播阶段的主从服务器因为网络原因而中断了复制,但从服务器通过自动重新连接上了主服务器,并继续复制主服务器。

    对于初次复制来说,旧版复制功能可以很好的完成任务,但是对于断线后重复制来说,旧版复制功能的效率很低。因为旧版的断线后重复制从服务器会向主服务器发送SYNC命令,而主服务器会将数据库中所有的键值对生成RDB文件,发送给从服务器。这种让主重服务器重新执行一次SYNC命令,这种做法是非常低效的。

2.2 新版重复制功能

    为了解决旧版功能断线时复制的低效问题,Redis从2.8版本开始,使用PSYNC命令来代替SYNC命令来执行复制时同步操作。PSYNC的完整重同步用于处理初次复制情况,完整重同步和SYNC命令的初次复制情况相同。而部分重同步则用于处理断线后的复制情况,断线后重连接时,主服务器将从服务器连接断开期间执行的写命令发送给从服务器,从服务器只接收这些命令,就可以将数据库更新至主服务器当前所处的状态。PSYNC的部分重同步功能由以下三个部分构成:

(1)复制偏移量:

    主从复制的双方,会分别维护一个复制偏移量server.master_repl_offset,主服务器每复制N个字节时,就将自己的复制偏移量加上N,从服务器每接收到N个字节时,就将自己的复制偏移量加上N。通过对比主从服务器的复制偏移量,程序可以很容易地知道主从服务器是否处于一致状态:

  • 如果主从服务器处于一致状态,那么主从服务器的偏移量总是相同的;
  • 相反,如果主从服务器的偏移量不同,那么说明主从服务器并未处于一致状态

(2)复制积压缓冲区:

    复制积压缓冲区是主服务器维护的一个固定长度的先进先出的队列server.backlog,默认大小为1M,也可以根据配置文件中的repl-backlog-size来设置。当主服务器进行命令传播时,它不仅会将写命令发送给所有重服务器,还会将写命令入队到复制积压缓冲区,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量。当从服务器重新连接上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:

  • 如果offset偏移量之后的数据仍然存在复制积压缓冲区中,那么主服务器将对从服务器执行部分重同步操作
  • 如果偏移量之后的数据已经不在复制积压缓冲区中,那么主服务器将对从服务器执行完整重同步操作。

(3)服务器运行ID:

    除了复制偏移量和复制积压缓冲区外,实现部分重同步还要用到服务器的运行ID server.runid,每个Redis服务器,不管主服务器还是重服务器,都会有自己的运行ID,当进行初次复制时,主服务器会将自己的运行ID发送给从服务器,从服务器会将这个ID保存起来。当从服务器断线重连接主服务器时,从服务器会向当前连接的主服务器发送之前保存的运行ID:

  • 主服务器收到这个ID后,如果和自己的ID相同,则尝试进行部分重同步;
  • 如果收到的ID和自己的运行ID不同,则主服务器进行完整重同步操作。

    断线后重复制由masterTryPartialResynchronization()函数实现,具体的流程如下图所示:

        

3. 主从同步的一致性分析

    首先从状态视角来看数据库,任何变更操作后,数据只有两种状态,所有副本一致或者不一致。在某些条件下,不一致的状态是暂时,还会转换到一致的状态,而那些永远不一致的情况几乎不会去讨论,所以习惯上大家会把不一致称为“弱一致”。相对的,一致就叫做“强一致”了。在工程实践中,实现状态视角的强一致性需要付出的代价太大,尤其是与可用性有无法回避的冲突,所以很多产品选择了状态视角的弱一致性。

    接下来看看 BASE 理论,它代表了三个特性,BA 表示基本可用性(Basically Available),S 表示软状态(Soft State),E 表示最终一致性(Eventual Consistency):

  • 基本可用性,是指某些部分出现故障,那么系统的其余部分依然可用。
  • 软状态或柔性事务,是指数据处理过程中,存在数据状态暂时不一致的情况,但最终会实现事务的一致性。
  • 最终一致性,在主副本执行写操作并反馈成功时,不要求其他副本与主副本保持一致,但在经过一段时间后这些副本最终会追上主副本的进度,重新达到数据状态的一致。

    NoSQL 产品是应用弱一致性的典型代表,但对弱一致性的接受仍然是有限度的,这就是 BASE 理论中的 E 所代表的最终一致性(Eventually Consistency),弱于最终一致性的产品就几乎没有了。

    redis的客户端发送命令给主服务器时:

  • 如果不发送WAIT命令,那么主服务器执行完成之后立即回复客户端命令的执行结果,并不等待命令同步到从服务器再回复,也就是说redis的主从同步其实是异步的,也就是需要经过一段时间后主从服务器的状态才能实现一致,这就是最终一致性。
  • 如果客户端发送WAIT命令,则客户端需要阻塞到master把命令同步到从服务器之后才能收到master的回复信息,也就是说,使用WAIT命令时,可以牺牲时间作为代价,实现强一致性。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值