Redis主從實現原理分析

 一, 實現原理圖

    (1)Slave服務器連接到Master服務器.

    (2)Slave服務器發送SYCN命令.

    (3)Master服務器備份數據庫到.rdb文件.

    (4)Master服務器把.rdb文件傳輸給Slave服務器.

    (5)Slave服務器把.rdb文件數據導入到數據庫中.

    上面的這5步是同步的第一階段, 接下來在Master服務器上調用每一個命令都使用replicationFeedSlaves()來同步到Slave服務器.

二, 實現細節

    (1) Slave服務器連接到Master服務器 發送SYNC命令:

        Slave服務器通過syncWithMaster()函數來連接Master服務器(如果Master服務器需要密碼登陸的話,先登陸), 並且發送SYNC命令請求同步, 接着打開rdb文件(用於存儲由Master發送過來的數據), 創建讀rdb的IO事件(readSyncBulkPayload). 代碼如下:

          int syncWithMaster(void) {

           ......

         //登陸master服務器

         if(server.masterauth) {

             syncWrite(fd, "AUTH xxx\r\n", strlen(server.masterauth)+7, 5);

             ......

         }

         //發送SYNC命令

         syncWrite(fd,"SYNC \r\n",7,5);

          ......

        //打開rdb文件

        dfd = open(tmpfile,O_CREAT|O_WRONLY|O_EXCL,0644);

        ......

        //創建讀rdb的IO事件

        aeCreateFileEvent(server.el, fd, AE_READABLE, readSyncBulkPayload, NULL);

         ......

        return REDIS_OK;

       }

    (2) Master服務器備份數據庫到.rdb文件:

        當Slave服務器發送SYNC命令到Master服務器時, Master服務器便會調用syncCommand()函數來進行同步. 同步的第一步是把數據庫的數據存儲爲rdb文件, 存儲完畢後調用     updateSlavesWaitingBgsave()函數來發送rdb文件給所有的Slave服務器.代碼如下:

        void syncCommand(redisClient *c) {

               //如果正在保存rdb文件

        if (server.bgsavechildpid != -1) {

         ......

    //主要判斷當前存儲rdb文件是不是由SYNC命令觸發的

    //如果當前存儲rdb文件不是由SYNC命令觸發, 則要等到下一次

    ......

     } else {//否則調用rdbSaveBackground()存儲rdb文件

    rdbSaveBackground(server.dbfilename);

    }

}

    當rdbSaveBackground()函數執行完畢, 就會調用updateSlavesWaitingBgsave()來發送rdb文件到所有的Slave服務器, 代碼如下:

void updateSlavesWaitingBgsave(int bgsaveerr) {

    listRewind(server.slaves,&li);

while((ln = listNext(&li))) {

    slave->repldbfd = open(server.dbfilename,O_RDONLY);

    .......

    aeCreateFileEvent(server.el,slave->fd,AE_WRITABLE, sendBulkToSlave,slave);

}

}

    updateSlavesWaitingBgsave()要做的事情是, 打開rdb文件, 創建發送rdb文件IO事件(sendBulkToSlave). sendBulkToSlave()主要的工作就是把rdb文件發送給Slave服務器.

    而當Slave服務器接收rdb文件完畢之後(readSyncBulkPayload()函數處理), 會清空原來數據庫的數據然後把rdb文件的數據導入到數據庫中.

(3) 增量同步

    完成上面的步驟之後, 同步基本完成接下來的工作就是增量同步也就是當Master服務器有數據更新的時候, 會立刻同步到所有的Slave服務器. replicationFeedSlaves()函數完成.

    當我們在Master服務器增減數據的時候, 就會觸發replicationFeedSlaves(), 代碼如下:

void call(redisClient *c, struct redisCommand *cmd) {

    ......

    if ((dirty || cmd->flags & REDIS_CMD_FORCE_REPLICATION) &&

        listLength(server.slaves))

        replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc);

    ......

}

    call()函數就是當用戶執行命令的時候觸發. dirty表示是否有數據更新, 如果有數據更新而且slave服務器不爲空, 就執行replicationFeedSlaves().

而replicationFeedSlaves()主要做的工作就是把用戶執行的命令發送到所有的Slave服務器, Slave服務器執行. 這樣就可以實施同步功能.

 

發佈了101 篇原創文章 · 獲贊 51 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章