Redis 主從複製架構
1. 單機版的侷限
單機版的侷限性在哪裏?
如果Redis
所在的實例宕機,那麼此時即不支持讀也不支持寫,這便是單機版存在的問題,可用性和併發性都是非常有限的。
爲解決上述的問題,從而引出了主從複製架構。
2. 主從複製架構分析
主從架構的示意圖如下
客戶端所有的寫操作都是通過 Master
實例來完成,所有的讀操作都是通過Slave
來完成的,從而實現讀寫分離。
優點
- 可用性提高。當
Master
宕機後,依然可以對Slave
讀操作。當一臺Slave
宕機,依然可以讀其他Slave
進行讀操作。 - 併發性提高。更準確的是對讀的併發性有所提高,可以對多臺的
slave
進行讀取,同時,當業務量劇增時,隨時可以增加新的Slave
。
缺點
- 可用性有限:當
Master
宕機後,此時寫操作都失敗,直到實例重啓。 - 數據同步問題:數據從
Master
同步到Slave
時,帶來的數據不一致性問題,該問題在分佈式鎖的環境下尤爲明顯。
3. 主從複製原理
slave
實例可以通過執行SLAVEOF
或設置slaveof <masterip> <masterport>
去複製master
服務器。
3.1 舊版複製功能的實現
Redis 2.8
版本以前複製的實現原理如下:
Redis
的複製功能分爲同步sync
和命令傳播command propagate
兩個操作:
- 同步:用於將從服務器的數據庫狀態更新至主服務器當前時刻數據庫狀態一致
- 命令傳播:用於主服務器的數據庫狀態被更改後,讓主從數據庫狀態一致
3.1.1 同步
從服務器發送SLAVEOF
命令後,首先會執行同步操作,從服務器通過發送SYNC
命令來完成,SYNC
命令的執行步驟:
-
從服務器向主服務器發送
SYNC
命令。 -
主服務器收到
SYNC
命令後執行BGSAVE
命令,在後臺生成一個RDB
文件,並使用緩衝區記錄從現在以後的所有寫命令(防止生成RDB
文件的同時,有新命令沒有寫入RDB
文件中)。 -
主服務器將
RDB
文件發送給從服務器,從服務器載入該RDB
文件。 -
主服務器將緩衝區裏面的命令發送給從服務器,從服務器執行這些寫命令,此時主從數據相互一致。
3.1.2 命令傳播
同步操作完成後,主從實例便處於一致性狀態,但是當有新的寫命令寫入到主實例,此時便又不一致了。
可見,通過命令傳播可以使主從重新處於一致性狀態。
3.1.3 舊版複製的缺陷
在從服務器第一次連接主服務器時,會進行RDB
的全量載入。但是由於網絡波動,導致主從斷開連接,當從服務器重新連接後,便又會重新發送SYNC
命令,進行RDB
的全量載入。如果1
分鐘內掉線三次,則會進行三次全量複製,可是1
分鐘內,Master
可能並不會新增很多數據,主從之間的數據差異性並不是很大,所以該方式很浪費資源。
3.2 新版複製功能的實現
爲了解決主從掉線後的複製權限,Redis 2.8
使用PSYN
代替SYNC
執行同步操作。
PSYNC
具有完整重同步和部分重同步兩種模式。
-
完整重同步:同
SYNC
命令的執行步驟一樣 -
部分重同步:用於處理斷線後重複製情況,主服務器可以將主從服務器連接斷開期間執行的寫命令發送給從服務器,從服務器只要接收並執行這些寫命令,就可以將數據庫更新至主服務器當前所處狀態。
3.3 部分重同步的實現
部分重同步功能由以下三個部分構成:
- 主服務器的複製偏移量(replication offset)和從服務器的複製偏移量
- 主服務器的複製積壓緩衝區(replication backlog)
- 服務器的與運行ID(run ID)
3.3.1 複製偏移量
主服務器和從服務器都維護一個複製偏移量。
- 主服務器每次向從服務器傳播N個字節的數據時,就將自己的複製偏移量的值加上N。
- 從服務器每次收到主服務器傳播來的N個字節的數據個給從服務器時,就將自己的複製偏移量的值加上N。
通過對比offset
的值,可以明顯知道兩者是否處於一致性狀態。
當處於不一致狀態時,便需要恢復A
丟失的數據,其是通過複製積壓緩衝區實現的。
3.3.2 複製積壓緩衝區
複製積壓緩衝區是主服務器維護的一個固定長度、先進先出的==隊列==,默認大小爲 1MB。
當主服務器進行命令傳播時,不僅會將命令發送給所有從服務器,還會將寫命令入隊到複製積壓緩衝區。
複製積壓緩衝區會爲隊列中的每個字節記錄相應的複製偏移量:
當從服務器重新連接上主服務器時,會通過 PSYNC
命令將自己的複製偏移量 offset
發送給主服務器,主服務器根據這個複製偏移量來決定對從服務器執行何種同步操作:
- 如果
offset
偏移量之後的數據仍然存在於複製積壓緩衝區,那麼主服務器採用部分重同步。 - 否則,採用完整重同步(因爲找不到寫命令,只能進行全量複製,所以可以更改緩衝區的大小,使命令被保存的時間更久,這樣就可以進行增量複製)。
3.3.3 服務器運行 ID
- 每個
Redis
服務器都有自己的運行ID
- 運行
ID
由服務器啓動自動生成,由40
個隨機的16
進制字符組成
當從服務器對主服務器進行初次複製時,主服務將自己的運行 ID
傳送給從服務器,從服務器會將這個運行 ID
保存起來。
當從服務器斷線重連上一個主服務器時,從服務器會向主從器發送之前保存的運行ID
。
如果運行ID
與主服務器的運行ID
相同,主服務器可以繼續嘗試執行部分重同步。
否則,說明主服務器不再是之前的主服務器,需要進行完整重同步操作。
當一個主服務器宕機後,那麼哨兵系統會選擇一個從服務器作爲主服務器,此時運行ID
便不一致了。