歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Redis源碼解析--Replication

Redis源碼解析--Replication

日期:2017/2/27 16:01:08   编辑:Linux教程
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的數量
Copyright © Linux教程網 All Rights Reserved