Redis主從複製原理剖析

RonTech 發表於 2020-11-19
Redis

生產環境中,為保證Redis快取系統的高可用性,對於Redis叢集,一般都會採用主從架構來實現讀寫分離。
那麼主從架構下,主節點和從節點間,資料是如何進行復制和同步,中間是一個什麼樣的流程,通過本文,想跟眾博友和同行一起討論。
首先看一個圖。
Redis主從複製流程圖

上圖展示了Slave節點第一次連線主節點和短暫斷開後再次連線主節點的情況下,資料複製的流程。

  1. 當從節點啟動後,會根據自身配置檔案中slave of配置的內容,得到主節點的IP和埠。
  2. 從節點向主節點傳送一個psync命令(如果主節點設定了祕鑰等安全資訊,則需帶上主節點的安全資訊),看看主節點是否能夠連線,如果主節點成功連線,則返回主節點的run id、offset資訊,同時觸發全量複製(Full Resynchronizztion)。
  3. 主節點fork一個子程式,執行bgsave命令,生成一個rdb快照。通過主節點中repl-diskless-sync、repl-diskless-sync-delay兩個配置,來選擇是否開啟無磁碟化的複製。
  • repl-diskless-sync預設為no,即不開啟無磁碟化配置,那麼在全量複製開始時,主節點fork出的子程式將rdb快照寫入磁碟。然後檔案通過父程式傳輸給排隊執行全量複製的所有Slave節點。

  • repl-diskless-sync如果設定為yes,即開啟無磁碟化複製,無磁碟化複製指的就是主程式fork子程式生成rdb快照檔案時,該檔案在主節點上不落盤,而是通過與Slave節點建立的socket連線直接將檔案推送給Slave節點。

  • repl-diskless-sync如果設定為yes,此時為了讓同一份rdb檔案能夠同時提供給更多的Slave節點,需配合repl-diskless-sync-delay這個配置一起使用。repl-diskless-sync-delay指的是在建立子程式生成rdb快照之前等待一段時間(如5秒),期望更多的子節點與主節點之間建立socket連線,生成rdb檔案之後可以一次性複製給多個子節點。

    在子程式生成rdb檔案並與從節點間程式rdb檔案資料複製的過程中,新寫入的資料,Redis將會儲存到緩衝區。緩衝區大小可以通過client-output-buffer-limit這個配置來設定。如:

client-output-buffer-limit slave 256MB 64MB 60
指的就是在rdb快照複製過程中,如果緩衝區資料超過256M,
或者持續60秒超過64M,則同步失敗。
  1. Redis子程式通過與從節點建立的Socket連線,直接傳送rdb快照給Slave節點。
  2. Slave節點收到rdb快照後落盤,然後進行載入。
  3. 從節點完成rdb快照載入之後,Redis傳送新寫入到緩衝區的資料寫命令給從節點,執行增量複製,保證主節點和從節點的資料一致。
  4. 後續每寫入一條資料,主節點都會非同步將該資料的寫命令傳送給從節點。
  5. 因為某些原因,從節點與主節點短暫斷開連線,導致主從節點資料不一致。
  6. 當slave連線到master,會執行PSYNC <runid> <offset>傳送記錄舊的master的runid(replication ID)和偏移量offset,這樣master能夠只傳送slave所缺的增量部分。但是如果master的複製積壓快取區沒有足夠的命令記錄,或者slave傳的runid(replication ID)不對,就會進行完整重同步,即slave會獲得一個完整的資料集副本。
  • 部分資料同步,需要的幾個參考值解釋如下:

  • run id(replication ID),每個Redis服務啟動時,都會生成一個run id,從節點與主節點建立連線後,主節點會將自己的run id同步給從節點。

  • offset:複製偏移量,主節點和從節點各自維護了一個複製偏移量,記錄傳輸的位元組數。當主節點向從節點傳送N個位元組資料時,主節點的offset增加N,從節點收到主節點傳來的N個位元組資料時,從節點的offset增加N。

  • replication backlog buffer,複製積壓緩衝區。是一個固定長度的FIFO佇列,大小由配置引數repl-backlog-size指定,預設大小1MB。需要注意的是該緩衝區由master維護並且有且只有一個,所有slave共享此緩衝區,其作用在於備份最近主庫傳送給從庫的資料。

  1. 主節點判斷,重新建立連線的從節點,符合部分資料同步的條件,將複製積壓緩衝區的資料寫命令,傳送給從節點,完成資料複製。

小結:

從上述的整個流程我們不難看出,使用Redis主從架構,基於其資料複製的規則和原理,為了資料的安全,我們必須開啟主節點的持久化功能,並且在資料量極大的情況下,一定要做好主節點資料備份的策略,避免因為主節點當機重啟而造成大量資料丟失,最後造成不可挽回損失。

因為如果主節點不做資料持久化,主節點當機重啟後,由於run id重啟後發生了變化,所有從節點重新與主節點建立連線後,都會全量同步主節點的資料,但是主節點沒有持久化資料,從節點也就全部都會被清空。

即使使用Redis哨兵,可以在主節點當機後將從節點提升為主節點,但是在某些極端情況下,假設你的伺服器允許自動重啟Redis程式,Redis主節點當機到重啟的這個短暫時間內,Redis哨兵還未來得及發現主節點下線,也會造成資料丟失。