1. 程式人生 > >面試題(redis master和slave是怎麼實現資料同步的)

面試題(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操作過程中資料同步的函式呼叫關係

圖中的序號表示呼叫的先後關係,同級之間的序號才有意義。