Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5783309
  • 博文数量: 291
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7924
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-06 14:28
个人简介

阿里巴巴是个快乐的青年

文章分类

全部博文(291)

文章存档

2018年(21)

2017年(4)

2016年(5)

2015年(17)

2014年(68)

2013年(174)

2012年(2)

分类: NOSQL

2013-03-21 16:49:21

        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_NONERedis.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主线程的定时任务serverCronRedis.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的数量,否则,结果你懂的。。。

    
阅读(7470) | 评论(1) | 转发(5) |
给主人留下些什么吧!~~

scq2099yt2013-03-21 19:01:08

文明上网,理性发言...