Redis的主从同步机制能够确保redis的master和slave之间的数据同步。按照同步内容的多少能够分为全同步和部分同步;按照同步的时机能够分为slave刚启动时的初始化同步和正常运行过程当中的数据修改同步;本文将对这两种机制的流程进行分析。redis
全备份过程当中,在slave启动时,会向其master发送一条SYNC消息,master收到slave的这条消息以后,将可能启动后台进程进行备份,备份完成以后就将备份的数据发送给slave,初始时的全同步机制是这样的:数据库
(1)slave启动后向master发送同步指令SYNC,master接收到SYNC指令以后将调用该命令的处理函数syncCommand()进行同步处理;缓存
(2)在函数syncCommand中,将调用函数rdbSaveBackground启动一个备份进程用于数据同步,若是已经有一个备份进程在运行了,就不会再从新启动了。socket
(3)备份进程将执行函数rdbSave()完成将redis的所有数据保存为rdb文件。函数
(4)在redis的时间事件函数serverCron(redis的时间处理函数是指它会定时被redis进行操做的函数)中,将对备份后的数据进行处理,在serverCron函数中将会检查备份进程是否已经执行完毕,若是备份进程已经完成备份,则调用函数backgroundSaveDoneHandler完成后续处理。性能
(5)在函数backgroundSaveDoneHandler中,首先更新master的各类状态,例如,备份成功仍是失败,备份的时间等等。而后调用函数updateSlavesWaitingBgsave,将备份的rdb数据发送给等待的slave。.net
(6)在函数updateSlavesWaitingBgsave中,将遍历全部的等待这次备份的slave,将备份的rdb文件发送给每个slave。另外,这里并非当即就把数据发送过去,而是将为每一个等待的slave注册写事件,并注册写事件的响应函数sendBulkToSlave,即当slave对应的socket可以发送数据时就调用函数sendBulkToSlave(),实际发送rdb文件的操做都在函数sendBulkToSlave中完成。3d
(7)sendBulkToSlave函数将把备份的rdb文件发送给slave。server
上述函数调用过程以下图1所示:blog
图1 redis全备份时master部分的的函数调用过程
2、数据修改操做的同步
Redis的正常部署中通常都是一个master用于写操做,若干个slave用于读操做,另外按期的数据备份操做也是单独选址一个slave完成,这样能够最大程度发挥出redis的性能。在部署完成,各master\slave程序启动以后,首先进行第一阶段初始化时的全同步操做,全同步操做完成以后,后续全部写操做都是在master上进行,全部读操做都是在slave上进行,所以用户的写操做须要及时扩散到全部的slave以便保持数据最大程度上的同步。Redis的master-slave进程在正常运行期间更新操做(包括写、删除、更改操做)的同步方式以下:
(1)master接收到一条用户的操做后,将调用函数call函数来执行具体的操做函数(此过程可参考另外一文档《redis命令执行流程分析》),在该函数中首先经过proc执行操做函数,而后将判断操做是否须要扩散到各slave,若是须要则调用函数propagate()来完成此操做。
(2)propagate()函数完成将一个操做记录到aof文件中或者扩散到其余slave中;在该函数中经过调用feedAppendOnlyFile()将操做记录到aof中,经过调用replicationFeedSlaves()将操做扩散到各slave中。
(3)函数feedAppendOnlyFile()中主要保存操做到aof文件,在该函数中首先将操做转换成redis内部的协议格式,并以字符串的形式存储,而后将字符串存储的操做追加到aof文件后。
(4)函数replicationFeedSlaves()主要将操做扩散到每个slave中;在该函数中将遍历本身下面挂的每个slave,以此对每一个slave进行以下两步的处理:将slave的数据库切换到本操做所对应的数据库(若是slave的数据库id与当前操做的数据id不一致时才进行此操做);将命令和参数按照redis的协议格式写入到slave的回复缓存中。写入切换数据库的命令时将调用addReply,写入命令和参数时将调用addReplyMultiBulkLen和addReplyBulk,函数addReplyMultiBulkLen和addReplyBulk最终也将调用函数addReply。
(5)在函数addReply中将调用prepareClientToWrite()设置slave的socket写入事件处理函数sendReplyToClient(经过函数aeCreateFileEvent进行设置),这样一旦slave对应的socket发送缓存中有空间写入数据,即调用sendReplyToClient进行处理。
(6)函数sendReplyToClient()的主要功能是将slave中要发送的数据经过socket发出去。
图二、redis操做过程当中数据同步的函数调用关系
图中的序号表示调用的前后关系,同级之间的序号才有意义。