面試題(redis master和slave是怎麼實現數據同步的)

Redis的主從同步機制可以確保redis的master和slave之間的數據同步。按照同步內容的多少可以分爲全同步和部分同步;按照同步的時機可以分爲slave剛啓動時的初始化同步和正常運行過程中的數據修改同步;本文將對這兩種機制的流程進行分析。

全備份過程中,在slave啓動時,會向其master發送一條SYNC消息,master收到slave的這條消息之後,將可能啓動後臺進程進行備份,備份完成之後就將備份的數據發送給slave,初始時的全同步機制是這樣的:

(1)slave啓動後向master發送同步指令SYNC,master接收到SYNC指令之後將調用該命令的處理函數syncCommand()進行同步處理;

(2)在函數syncCommand中,將調用函數rdbSaveBackground啓動一個備份進程用於數據同步,如果已經有一個備份進程在運行了,就不會再重新啓動了。

(3)備份進程將執行函數rdbSave()完成將redis的全部數據保存爲rdb文件。

(4)在redis的時間事件函數serverCron(redis的時間處理函數是指它會定時被redis進行操作的函數)中,將對備份後的數據進行處理,在serverCron函數中將會檢查備份進程是否已經執行完畢,如果備份進程已經完成備份,則調用函數backgroundSaveDoneHandler完成後續處理。

(5)在函數backgroundSaveDoneHandler中,首先更新master的各種狀態,例如,備份成功還是失敗,備份的時間等等。然後調用函數updateSlavesWaitingBgsave,將備份的rdb數據發送給等待的slave。

(6)在函數updateSlavesWaitingBgsave中,將遍歷所有的等待此次備份的slave,將備份的rdb文件發送給每一個slave。另外,這裏並不是立即就把數據發送過去,而是將爲每個等待的slave註冊寫事件,並註冊寫事件的響應函數sendBulkToSlave,即當slave對應的socket能夠發送數據時就調用函數sendBulkToSlave(),實際發送rdb文件的操作都在函數sendBulkToSlave中完成。

(7)sendBulkToSlave函數將把備份的rdb文件發送給slave。

上述函數調用過程如下圖1所示:

圖1 redis全備份時master部分的的函數調用過程

二、數據修改操作的同步

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發出去。

圖2、redis操作過程中數據同步的函數調用關係

圖中的序號表示調用的先後關係,同級之間的序號纔有意義。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章