引言
我們之前操作 Redis 都是單機版,但是實際應用中沒人使用單機版,都是搭建叢集的方式。這篇文章要介紹的主從複製,是指將一臺 Redis 伺服器的資料,複製到其他 Redis 伺服器,我們將前者稱為主節點 master,將後者稱為從節點 slave(replica)。在這個過程中,資料的複製是單向的,即只能從主節點到從節點。並且從節點只能讀資料,不能寫資料,實現讀寫分離。
- 一個主節點可以有多個從節點,一個從節點只能有一個主節點。所有的伺服器預設都是主節點。
- 從節點下面還可以有從節點,形成一個圖的結構
主從複製的優點
- 資料冗餘:主從複製實現了資料的熱備份,是持久化之外的一種資料冗餘方式。
- 故障恢復:當主節點出現問題時,可以由從節點提供服務,實現快速的故障恢復;實際上是一種服務的冗餘。
- 負載均衡:在主從複製的基礎上,配合讀寫分離,寫資料時應用連線主節點,讀資料時應用連線從節點,分擔伺服器負載;尤其是在寫少讀多的場景下,通過多個從節點分擔讀負載,可以大大提高 Redis 伺服器的併發量。
- 高可用基礎:主從複製是哨兵模式和叢集能夠實施的基礎。
開啟主從複製
主從複製的前提就是多個 Redis 伺服器,因此要多個配置檔案。
開啟主從複製的方式有兩種:
配置配置檔案
從伺服器配置master節點
# 主節點ip port
# replicaof <masterip> <masterport>
# 主節點的認證密碼(可選)
# masterauth <master-password>
顯示命令開啟
客戶端使用該命令
slaveof [ip] [port]
INFO REPLLICATION
命令可檢視節點資訊
主從複製的具體實現
主從複製的實現可以分為三個階段:建立連線、資料同步、命令傳播
1. 建立連線
傳送 slaveof 非同步命令
建立 socket 連線
連線成功後從節點會為該 socket 建立一個專門處理複製工作的檔案事件處理器,負責後續的複製工作,如接收 RDB 檔案、接收命令傳播等;
傳送 ping 命令
從節點成為主節點的客戶端之後,傳送 ping 命令進行首次請求,目的是檢查 socket 連線是否可用,以及主節點當前是否能夠處理請求。
身份驗證
取決於從節點是否設定 masterauth 選項,而這個又取決於主節點是否設定 requirepass
傳送從節點埠資訊
2. 資料同步
連線建立完成後,要進行主從資料庫的資料同步,這一步也可以看作從節點中資料的初始化。資料同步有舊版和新版兩種。我們先來看看舊版 Redis 怎麼實現資料同步的
舊版同步
舊版同步使用的命令是 sync,同步方式是全量複製。以下是sync命令的執行步驟:
- 從伺服器向主伺服器傳送 sync 命令
- 主伺服器收到後執行 BGSAVE 的rgb持久化命令,生成一個RDB檔案。並用一個緩衝區記錄生成rgb檔案期間執行的所有寫命令。
- 主伺服器將 rgb 檔案傳送給從伺服器,從伺服器載入整個 rgb 檔案,同步資料庫狀態至執行 BGSAVE 時主服務的狀態
- 主伺服器將記錄在緩衝區的寫命令傳送給從伺服器。至此從服務和主服務資料庫狀態達到一致。
舊版同步功能的缺陷:
斷線後重連需要再次傳送 sync 同步命令,主服務再次生成 rgb 檔案,從伺服器載入恢復資料庫狀態。簡單來說就是斷線後又要全量複製,但其實沒有必要,我們只需要同步斷線後到重新連線期間主伺服器變化的資料即可。
並且,sync是一個非常耗費資源的操作,因此舊版同步是非常低效的。
新版同步
新版同步使用的命令是 psync ,同步方式是增量複製
psync 有完成重同步和部分重同步兩種模式。完整重同步其實就是sync,主要用於處理初次服務的情況。部分重同步主要用於處理斷線後重連的情況。部分重同步不需要重新生成、傳送和載入整個rdb檔案,只需要將從伺服器缺少的寫命令傳送給從伺服器執行即可。完美的解決了舊版同步效率低下的問題。
下面我們就來看看他是如何實現的?首先要了解幾個概念
主節點和從節點的複製偏移量
主節點和從節點會各自維護一個複製偏移量 ( offset ),代表的是主節點向從節點傳遞的位元組數。主節點每次向從節點傳播 N 個位元組資料時,主節點的 offset 增加 N;同理,當從節點每次收到主節點傳來的 N 個位元組資料時,從節點的 offset 增加 N。通過比較主節點和從節點的 offset 可以判斷資料庫狀態是否一致,如果兩者的 offset 相同,則一致,否則不一致;也可以根據兩者的 offset 找出從節點缺少的資料。
主節點的複製積壓緩衝區
主節點維護的一個固定長度的先進先出 ( FIFO) 佇列,預設大小是 1MB。當主節點開始有從節點時,主節點建立複製積壓緩衝區,其作用是備份主節點最近傳送給從節點的資料。儲存的資料有兩方面:
- 最近傳播的寫命令。為什麼最近呢?因為佇列長度固定,老的命令會被新的取代
- 佇列中每個位元組對應的複製偏移量
當從伺服器重新連線上主機時,會通過 psync 命令把自己的offset發給主節點
- 如果offset之後的資料全部在佇列中,執行部分重同步(增量複製)
- 如果offset之後的資料不全在佇列中,執行完整重同步(全量複製)
伺服器的執行ID
每個redis伺服器都有自己的執行ID,在啟動時自動生成,由40個隨機的十六進位制字元組成
當從節點對主節點進行初次複製時,主節點會把自己的執行ID發給從節點,從節點儲存並在斷線重連上主節點時把次ID傳送給主節點:
- 如果此ID和主節點當前連線的ID一致,說明是同一個狀態,可以進行增量複製
- 如果不同,只能進行全量複製
總結:psync的執行流程:
命令傳播
資料同步階段完成後,主從節點進入命令傳播階段;在這個階段主節點將自己執行的寫命令傳送給從節點,從節點接收命令並執行,從而保證主從節點資料的一致性。
這個階段沒啥可說的,就是把主節點執行的寫命令發給從節點,讓它們也執行一次,保證主從一致性
心跳檢測
在命令傳播階段,從伺服器預設會以每秒一次的頻率向主伺服器傳送replconf ACK <replication_offset>
,其中replication_offset
是它當前的複製偏移量。心跳檢測的目的有以下三個:
檢測主從伺服器的網路連線狀態
如果主伺服器超過一秒沒有收到從服務的該命令,那麼主伺服器就知道主從連接出現問題了
輔助實現min-slaves
主服務我們可以配置以下兩個引數來防止不安全寫命令傳播,如果從節點小於3或者lag大於10,主伺服器都會拒絕執行寫命令
# 從節點的最小個數
# min-replicas-to-write 3
# 從節點lag的最大值
# min-replicas-max-lag 10
檢測命令丟失
上面說過心跳檢測命令還發送了從節點自身儲存的offset。主節點會與自己的 offset 進行對比,如果從節點資料缺失(如網路丟包),主節點會找到缺失的資料,並把這些資料重新推送給伺服器(這裡也會利用複製積壓緩衝區)。
參考