Redis | 第11章 伺服器的複製《Redis設計與實現》
阿新 • • 發佈:2021-12-08
目錄
新人制作,如有錯誤,歡迎指出,感激不盡!
歡迎關注公眾號,會分享一些更日常的東西!
如需轉載,請標註出處!
前言
參考資料:《Redis設計與實現 第二版》;
第四部分為多機資料庫的實現,主要由以下模組組成:複製、Sentinel、叢集;
本篇將介紹 Redis 的複製功能。在 Redis 中,使用者可以通過執行 SLAVEOF 命令或者設定 salveof
選項,讓一個從伺服器複製主伺服器。
與本章相關的 Redis 命令總結在下篇文章,歡迎點選收藏,本篇將不再重複:
《Redis常用命令及示例總結(API)》:https://www.cnblogs.com/dlhjw/p/15639773.html
1. 舊版複製功能的實現
- 舊版指 Redis 2.8 以前版本;
- 舊版 Redis 的複製功能分為同步(sync)和命令傳播(command propagate)兩個操作:
- 同步操作:將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態;
- 命令傳播:用於在主伺服器的資料庫狀態被修改,導致主從伺服器的資料庫狀態出現不一致時,讓主從伺服器重新回到一致狀態;
1.1 同步與命令傳播
- 同步:客戶端向從伺服器傳送 SLAVEOF 命令,從伺服器執行以下步驟:
- 1)從伺服器向主伺服器傳送 SYNC 命令;
- 2)收到 SYNC 命令的主伺服器執行 BGSAVE 命令,在後臺生成 RDB 檔案,並使用一個緩衝區記錄此刻開始執行的所有寫命令;
- 3)當主伺服器完成 BGSAVE 命令時,將 RDB 檔案發給從伺服器,從伺服器接收並載入 RDB 檔案,將資料庫狀態更新至主伺服器執行 BGSAVE 命令時的資料庫狀態;
- 4)主伺服器將記錄在緩衝區裡的所有寫命令傳送給從伺服器,從伺服器執行寫命令,將資料庫狀態更新至主伺服器資料庫當前的狀態;
- 命令傳播:同步操作完成後,主伺服器會將自己執行的寫命令,傳送給從伺服器。從伺服器執行後,主從伺服器資料庫狀態再次回到一致狀態;
1.2 舊版複製功能的缺陷
- 處於命令傳播階段的主從伺服器因為網路問題中斷了複製,重連後從伺服器會向主伺服器傳送 SYNC 命令執行同步操作;
- SYNC 命令非常消耗資源:
- 主伺服器執行 BGSAVE 命令時:耗費主伺服器大量 CPU、記憶體與磁碟 IO 資源;
- 主伺服器將 RDB 檔案發給從伺服器時:耗費主從伺服器大量網路資源(流量和頻寬),並對主伺服器響應命令請求的時間產生影響;
- 從伺服器在載入 RDB 檔案期間:從伺服器阻塞無法處理命令請求;
2. 新版複製功能的實現
- 新版指 Redis 2.8 以後版本;
- 新版 Redis 的複製功能分為完全重同步(full resynchronization)和部分重同步(partial resynchronization)兩個操作:
- 完全重同步:與初次複製情況相同;
- 部分重同步:主伺服器僅將主從伺服器在斷線期間的寫命令發給從伺服器。使用 PSYNC 命令;
2.1 部分重同步的實現原理
- 部分重同步依賴以下三個部分:
- 主從伺服器的複製偏移量(replication offset);
- 主伺服器的複製積壓緩衝區(replication backlog);
- 主伺服器的執行 ID(run ID);
- 複製偏移量:
- 執行復制的主從伺服器雙方會分別維護一個複製偏移量;
- 主伺服器每次向從伺服器傳播 N 個位元組資料時,將自己的複製偏移量加 N;
- 從伺服器每次收到主伺服器傳播來的 N 位元組資料時,將自己的複製偏移量加 N;
- 通過對比主從伺服器的複製偏移量可以判斷其資料庫狀態是否一致;
- 複製積壓緩衝區
- 複製積壓緩衝區由主伺服器維護一個固定長度(fixed-size)先進先出(FIFO)佇列,預設大小 1MB;
- 當主伺服器進行命令傳播時,會將命令發給從伺服器,同時將寫命令入隊到複製積壓緩衝區;
- 重連時,從伺服器將自己的複製偏移量
offset
發給主伺服器:- 若
offset+1
的內容在複製積壓緩衝區內,主伺服器對從伺服器執行部分同步操作; - 反之,執行完整重同步操作;
- 若
- 複製積壓緩衝區的大小可以根據公式估算:
second * write_size_per_second
:- second 為從伺服器斷線後重連所需平均時間;
- write_size_per_second 為主伺服器平均每秒產生的寫命令資料量;
- 為安全起見,可將複製積壓緩衝區大小設定為:上述公式乘 2;
- 伺服器執行 ID:
- 每個 Redis 都有自己的執行 ID;
- 執行 ID 在伺服器啟動時自動生成,由 40 個隨機的十六進位制字元組成;
- 從伺服器對主伺服器進行初次複製時,主伺服器會將自己的執行 ID 傳送給從伺服器,從伺服器將這個 ID 儲存;
- 重連時,從伺服器向主伺服器傳送儲存的執行 ID:
- 若從伺服器儲存的 ID 為當前主伺服器執行 ID,根據偏移量判斷重同步方式;
- 反之,執行完整重同步操作;
3. PSYNC 命令的實現
- PSYNC 命令的呼叫方法有兩種:
- 從伺服器沒有複製過任何主伺服器,或之前執行過 SLAVEOF NO ONE 命令,那麼從伺服器在開始複製時向主伺服器傳送 PSYNC ? -1 命令,主動請求主伺服器進行完整重同步;
- 反之,從伺服器傳送 PSYNC master_run_id offset 命令,
master_run_id
為上一次複製時的主伺服器的執行 ID,offset
為從伺服器當前的複製偏移量;
- 主伺服器接受 PSYNC 命令後會產生三種回覆:
- 完整重同步:返回
+FULLRESYNC master_run_id offset
,master_run_id 為當前主伺服器的執行 ID,offset 為主伺服器當前的複製偏移量; - 部分重同步:返回
+CONTINUE
,從伺服器只需要等待主伺服器傳送自己缺少的部分資料; - 錯誤:返回
-ERR
,主伺服器版本低於 Redis 2.8,從伺服器傳送 SYNC 命令,與主伺服器執行完整同步操作;
- 完整重同步:返回
4. 複製的詳細步驟
- 該步驟為 Redis 2.8 版本以上
- 客戶端向從伺服器傳送 SLAVEOF ip port 命令;
4.1 設定主伺服器的地址和埠
-
從伺服器將 SLAVEOF ip port 命令的 ip 地址和 port 埠儲存到伺服器狀態裡:
struct redisServer{ //... //主伺服器的地址 char *masterhost; //主伺服器的埠 int masterport; };
-
從伺服器向客戶端返回 OK,然後開始複製;
4.2 建立套接字連線
- 從伺服器根據
masterhost
和masterport
建立連向主伺服器的套接字連線; - 套接字連線(connect)成功後,從伺服器為該套接字關聯一個檔案事務處理器,專門用來處理複製工作;
- 主伺服器接受(accept)從伺服器的套接字連線後,為該套接字建立響應客戶端狀態,並將從伺服器視作客戶端(client);
- 此時,從伺服器具備伺服器(server)和客戶端(client)雙重身份;
4.3 傳送 PING 命令
- 從伺服器成為主伺服器的客戶端後,向主伺服器傳送 PING 命令;
- PING 命令的作用:
- 檢查套接字的讀寫狀態是否正常;
- 檢查主伺服器能否正常處理命令請求;
- 主伺服器對 PING 命令的回覆有 3 種情況:
- 返回命令回覆:但從伺服器不能在有限時間內讀取命令內容,表示主從伺服器間網路連線狀態不佳。此時從伺服器會斷開並重新建立連向主伺服器的套接字;
- 返回錯誤:表示主伺服器暫時沒法處理從伺服器的命令請求。此時從伺服器會斷開並重新建立連向主伺服器的套接字;
- 返回
PONG
:表示網路連線正常,從伺服器可以繼續執行復制工作;
4.4 身份驗證
-
從伺服器收到 PONG 回覆後,根據是否設定
masterauth
選項決定是否進行身份驗證;- 從伺服器沒有設定 masterauth 選項,不進行身份驗證;
- 從伺服器設定了 masterauth 選項,需要進行身份驗證;
-
在需要進行身份驗證的情況下,從伺服器給主伺服器傳送 AUTH password 命令,命令的引數
password
為從伺服器masterauth
選項的值; -
從伺服器在進行身份驗證時根據:主伺服器的
requirepass
選項和從伺服器的masterauth
選項不同,可能遇到以下情況:主伺服器的 requirepass
選項從伺服器的 masterauth
選項情況 設定 設定 相同則繼續複製,不同則返回 invalid password 錯誤 設定 沒有設定 返回 NOAUTH 錯誤 沒有設定 設定 返回 no password is set 錯誤 沒有設定 沒有設定 繼續複製工作
4.5 傳送埠資訊
-
身份驗證完成後,從伺服器執行 REPLCONF listening-port port-number 命令,向主伺服器傳送自己的監聽埠號
port-number
; -
主伺服器接受後,儲存在從伺服器的客戶端狀態
slave_listening_port
屬性中:typedef struct redisClient{ //... // 從伺服器的監聽埠號 int slave_listening_port; } redisClient;
4.6 同步
- 從伺服器向主伺服器傳送 PSYNC 命令,執行同步操作,將自己的資料庫更新至主伺服器資料庫當前所處的狀態;
- 在同步操作執行完後,主伺服器會成為從伺服器的客戶端,理由如下:
- 完整重同步情況:需要將緩衝區裡的寫命令傳送給從伺服器;
- 部分重同步情況:需要將複製積壓緩衝區裡的寫命令傳送給從伺服器;
4.7 命令傳播
- 完成同步後,主伺服器進入命令傳播階段,一直將寫命令發給從伺服器;
5. 心跳檢測
- 在命令傳播階段,從伺服器預設每秒向主伺服器傳送命令 REPLCONF ACK replication_offset,
replication_offset
引數是從伺服器當前的複製偏移量; - 心跳檢測的三個作用:
- 檢測主從伺服器的網路連線狀態:使用 INFO replication 命令可以檢視從伺服器最後一次向主伺服器傳送 REPLCONF ACK 命令距離現在過了多久,一般在 0~1 秒為正常;
- 輔助 min-slaves 配置選項:當從伺服器數量 x 少於
min-slaves-to-write
屬性值或 x 個伺服器的延遲大於等於min-slaves-max-lag
屬性值時,主伺服器拒絕寫命令; - 檢測命令丟失:當主伺服器發現從伺服器的
replication_offset
引數與自己的不一致時,補發寫命令資料;
- 補發命令資料與部分重同步的區別在於:前者沒有斷線,後再斷線了;
- Redis 2.8 版本以前沒有補發命令資料功能;