Redis源码解析--Replication

         Redis的复制功能是基于内存快照即rdb的,也就是说 无论使用哪种持久化机制,只要用到了复制功能,master 都会产生内存快照即rdb,slave接收rdb以同步数据 。Redis完成复制的源码主要分布在Replication.c(共610行中,分用于master和slave的函数,下面会详述过程。
一、状态
        Redis复制时slave和master都分别是一个状态机,状态定义在 Redis.h(162~179)中,主要状态如下:
/* Slave replication state - slave side */
#define REDIS_REPL_NONE 0 /* No active replication */
#define REDIS_REPL_CONNECT 1 /* Must connect to master */
#define REDIS_REPL_CONNECTING 2 /* Connecting to master */
#define REDIS_REPL_TRANSFER 3 /* Receiving .rdb from master */
#define REDIS_REPL_CONNECTED 4 /* Connected to master */


/* Synchronous read timeout - slave side */
#define REDIS_REPL_SYNCIO_TIMEOUT 5


/* Slave replication state - from the point of view of master
 * Note that in SEND_BULK and ONLINE state the slave receives new updates
 * in its output queue. In the WAIT_BGSAVE state instead the server is waiting
 * to start the next background saving in order to send updates to it. */
#define REDIS_REPL_WAIT_BGSAVE_START 3 /* master waits bgsave to start feeding it */
#define REDIS_REPL_WAIT_BGSAVE_END 4 /* master waits bgsave to start bulk DB transmission */
#define REDIS_REPL_SEND_BULK 5 /* master is sending the bulk DB */
#define REDIS_REPL_ONLINE 6 /* bulk DB already transmitted, receive updates */
        slave和master状态转换如下图:
图1 slave状 态机


图2 master状态机

        
二、 流程

图3 复制时序图
         从上图可以看出整个状态(红色部分为状态)转换流程如下:
         (1)初始情况下slave和master都处于REDIS_REPL_NONE ( Redis.c/initServerConfig()/1081 ) 状态 。
         (2)slave从配置文件中读取或者从客户端接收到 slave of指令,slave 状态转换为REDIS_REPL_CONNECT(Replication.c/slaveofCommand/538 )。
         (3)slave在 定时任务serverCron( Redis.c/906 ) 中调用replicationCron(Replicaltion.c/547)以连接master(Replication.c/connectWith),连接成功后slave状态转换为REDIS_REPL_CONNECTING(Replication.c/connectWithMaster/486)。
        (4)slave发送sync命令给master(Replication.c/syncWithMaster/426),slave状态转换为REDIS_REPL_TRANSFER(Replication.c/syncWithMaster/426)。
        (5)slave打开临时rdb文件用于存储即将要发送过来的快照数据(Replication.c/syncWithMaster/436),注册事件readSyncBulkPayLoad(Replication.c/syncWithMaster/446)用于接收快照数据,然后等待master发送回内存快照文件。
         (6)master收到sync命令后会跳转到syncCommand(Replication.c/83)函数,master状态转换为REDIS_REPL_BGSAVE_START(Replication.c/syncCommand/128),syncCommand函数判断是否有正在进行内存快照的子进程,如果有则等待其结束,没有则调用rdbSaveBackground(Rdb.c/685)函数立即开始内存快照,当快照完成后将master状态转换为REDIS_REPL_WAIT_BGSAVE_END(Replication.c/syncCommand/139)。
        (7)master主线程的定时任务serverCron(Redis.c/906)会检测做快照的子进程是否退出(Redis.c/serverCron/853),如果退出了则调用backgroundSaveDoneHandler(Redis.c/serverCron/854)函数,backgroundSaveDoneHandler会处理一些快照后的收尾工作,然后调用updateSlavesWaitingBgsave(Replication.c/208)函数。
        (8)master在函数updateSlavesWaitingBgsave中打开前面快照生成的rdb文件(Replication.c/updateSlavesWaitingBgsave/228),将master状态转换为REDIS_REPL_SEND_BULK(Replication.c/updateSlavesWaitingBgsave/236),并注册事件sendBulkToSave(Replication.c/updateSlavesWaitingBgsave/238)用于读取并发送上面打开的rdb快照数据给slave(Replication.c/148),发送完毕后将master状态转换为REDIS_REPL_ONLINE(Replication.c/sendBulkToSlave/191)。
         (9)slave通过步骤5中注册的事件readSyncBulkPayload来接收master发送的rdb数据(Replication.c/275),保存到本地,待接收完成后,调用emptydb(Db.c/140)以清空整个数据库,调用rdbLoad(Rdb.c/1015)重新读取master发送过来的内存快照文件以重建内存数据结构,并将状态置为REDIS_REPL_CONNECTED(Replication.c/readSyncBulkPayload/355),slave状态机转换完成,等待增量数据。
         (10)master在发送快照文件的过程中,接收的任何会改变数据集的命令都会暂时先保存在slave网络连接的发送缓存队列里(list数据结构 ),待快照完成后,依次发给slave。slave和master之间有心跳检测和超时退出。

、缺陷
        Redis的复制机制不支持增量复制,在slave连接master时,master需要进行内存快照,然后将整个快照数据发给slave,这会给master带来很大压力,slave接收完快照数据后会先清空数据库,再重建整个数据结构,这会导致数据大时slave同步时间非常长,所以需要注意slave和master之间的网络要非常稳定,不会闪断,否则这个过程会非常悲剧,因此,slave和master之间跨IDC机房或者南北电信都会有很大风险。另外,最好一开始就规划好slave的数量,否则,结果你懂的。。。

    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值