上一篇寫了Redis複製功能的簡單應用,下面我們看下Redis複製功能的實現過程。下面基本上是理論部分,枯燥乏味,但希望大家能看看,畢竟知識不都是感興趣的.
耐得住寂寞,經得起誘惑,方能守得住繁華 ~.~
舊版複製功能的實現
Redis的複製功能分為同步和命令傳播兩個操作:
1、同步操作用於將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態。
2、命令傳播操作則用於在主伺服器 的資料庫狀態被修改,導致從伺服器的資料庫狀態出現不一致時,讓主伺服器的資料庫重新回到一致狀態。
從伺服器對主伺服器的同步操作需要通過向主服務傳送sync命令來完成,以下是sync命令的執行步驟:
(1)從伺服器向主伺服器傳送SYNC命令
(2)收到SYNC命令的主伺服器執行BGSAVE命令,在後臺生成一個RDB檔案,並使用一個緩衝區記錄現在開始執行的所有寫命令。
(3)當主伺服器的BGSAVE命令執行完畢時,主伺服器會BGSAVE命令生成的RDB檔案傳送給從伺服器,從伺服器接收並載入這個RDB檔案,將自己的資料庫狀態更新至主伺服器執行BGSAVE命令時的資料庫狀態。
(4)主伺服器將記錄在緩衝區的所有寫命令傳送給從伺服器,從伺服器執行這些寫命令,將自己的資料庫狀態更新至主伺服器資料庫當前所處的狀態。
命令傳播:當主伺服器執行客戶端寫命令時,主伺服器的資料庫就有可能被修改,並導致主從不一致。此時主伺服器會將自己執行的寫命令傳送給從伺服器執行,當從伺服器執行了相同的寫命令後,主從伺服器再次回到一致狀態。
缺陷:
1、初始複製從伺服器從來沒有複製過任何主伺服器或者從伺服器當前要複製的主伺服器和上次複製的主伺服器不同。
2、斷線後重複製:處於命令傳播階段的主從伺服器因為網路原因而中斷了複製,但從伺服器通過自動連線從新連上主伺服器,並繼續複製。
新版複製功能的實現(PSYNC代替SYNC)
PSYNC命令具有完整重同步和部分重同步兩種模式:
(1)完整重同步用於處理初次複製功能,與SYNC功能基本一致;
(2)部分重同步用於處理斷線後重復值的情況,解決舊版效率低的問題。
部分重同步的實現:
(1)主伺服器與從伺服器都會維護一個複製偏移量
(2)複製積壓緩衝區是由主伺服器維護的一個固定長度先進先出的佇列預設(1MB),發生斷線從連時,但從伺服器重連上主伺服器,從伺服器會通過PSYNC命令將自己的複製偏移量offset傳送給主伺服器,主伺服器根據這個複製偏移量來決定對從伺服器執行何種同步操作:如果offset偏移量之後的資料仍然存在於複製積壓緩衝區裡面,那麼主伺服器將對從伺服器執行部分重同步,反之,執行完整重同步操作。除了複製偏移量和複製積壓緩衝之外,實現部分重同步還需要用到伺服器執行ID:每個Redis伺服器都有自己啟動時生成的由40個隨機16進位制字元組成的執行ID。當伺服器對主伺服器進行初次複製時,主伺服器會將自己的執行ID傳送給從伺服器,而從伺服器則會將這個執行ID儲存起來。當伺服器斷線重連時,從伺服器向主伺服器傳送儲存的執行ID,如果ID一樣,則主伺服器嘗試部分重同步操作,如果不同,則執行完整重同步操作。
PSYNC命令的實現
PSYNC命令的實現方法有兩種:
(1)如果從伺服器以前沒有複製過任何主伺服器,或者之前執行過SLAVEOF no one命令,那麼從伺服器在開始新的複製時將向主伺服器傳送PSYNC? -1 命令,主動請求進行完整重同步。
(2)如果之前複製過某個主伺服器,那麼從服務在開始一次新的複製時向主伺服器傳送PSYNC <runid> <offset>。由主伺服器來判斷該用那種方式同步。
(3)如果主伺服器返回+FULLRESYNC回覆,則表示主伺服器將與從伺服器執行完整重同步。
(4)如果主伺服器返回+CONTINUE回覆,則表示部分重同步,從伺服器只需等待接收資料即可。
(5)主伺服器返回-ERR回覆,則表示主伺服器版本低於2.8不識別PSYNC命令從伺服器將向主伺服器傳送SYNC命令完成同步操作。
複製的實現
1、執行SLAVEOF ip port命令,此時從伺服器首先將ip與埠儲存到伺服器狀態的masterhost屬性與masterport屬性裡面,並向客戶端返回“OK”,表示命令已經被接收。
2、建立套接字連線
從伺服器根據ip與埠建立連向主伺服器的套接字,如果套接字連線到主伺服器,那麼從伺服器將為這個套接字關聯一個 專門用於處理複製工作的檔案事件處理器,這個事件處理器負責執行後續複製工作。主伺服器在接受從伺服器的套接字連線後,將為該套接字建立相應的客戶端狀態,並將從伺服器看作一個連線到主伺服器的客戶端對待,
3、傳送PING命令
作用:
(1)雖然與主伺服器建立套接字連線,但雙方並未使用該套接字進行任何通訊,檢查套接字讀寫是否正常。
(2)檢查主伺服器是否能夠正常處理命令請求。
傳送命令後可能遇到的三種情況:
(1)超時,在規定的時間限制內從伺服器未收到回覆內容,此時從伺服器斷線重連。
(2)如果主伺服器向從伺服器回覆一個錯誤,表示主伺服器暫時無法處理從伺服器的命令請求,從伺服器斷線重連。
(3)收到正常回復內容,則可以進行下一步操作。
4、身份驗證(如果從伺服器設定了masterauth選項)
從伺服器向主伺服器傳送一條AUTH命令,此時從伺服器可能遇到的情況有:
(1)主伺服器沒有設定requirepass選項,並且從伺服器沒有設定master選項,那麼從伺服器將繼續執行從服務傳送的命令,複製操作繼續。
(2)如果從伺服器通過AUTH命令傳送的密碼與主伺服器requirepass設定的密碼相同,那麼主伺服器將繼續執行從伺服器傳送的命令,如果不同則主伺服器返回一個invalid password錯誤。
(3)如果主伺服器設定了requirepass選項,但從伺服器沒有設定masterauth選項,那麼主伺服器將返回一個NOAUTH選項。另一方面如果主伺服器沒有設定requirepass選項,但伺服器設定了masterauth選項,那麼主伺服器將返回一個no password is set 錯誤。
5、傳送埠資訊,從伺服器將執行REPLCONF listening-port port命令,向主伺服器傳送從伺服器的監聽埠號,主伺服器將埠號儲存在對應的客戶端狀態slave_listening_port屬性中。
6、同步
7、命令傳播
主伺服器將自己執行的寫命令傳送給從伺服器,從伺服器只要一直執行主伺服器發來的命令即可。
心跳檢測
在命令傳播階段,從伺服器預設以每秒一次的頻率向主伺服器傳送命令:
REPLCONF ACK <replication_offset>
作用:檢測主伺服器的網路連線狀態;輔助實現min-slaves選項;檢測命令丟失。
Redis的min-slaves-to-write和min-slaves-max-lag兩個選項防止主伺服器在不安全的情況下執行寫命令。
當從伺服器小於min-slaves-to-write或者min-slaves-to-write個數量的伺服器延遲lag值都大於等於min-slaves-max-lag時,主伺服器將拒絕執行寫命令。
如果因為網路故障,主伺服器傳播給從伺服器的寫命令半路丟失,那麼當從伺服器向主伺服器傳送REPLCONF ACK命令時,主伺服器將發覺從伺服器當前的複製偏移量少於自己的偏移量,主伺服器就會根據從伺服器提交的複製偏移量,在複製積壓緩衝區裡面找到從伺服器缺少的資料,並將這些資料重新傳送給從伺服器。
每天學一點,總會有收穫。
下一步我們看下Redis的Sentinel(哨兵)
說明:尊重作者智慧財產權,文中內容參考《Redis設計與實現》,僅在此做學習與大家分享。