1. 程式人生 > 其它 >Redis設計與實現 第 15 章 複製

Redis設計與實現 第 15 章 複製

第 15 章 複製

執行 SLAVOF 命令或設定 slaveof 選項,讓一個伺服器去複製另外一個伺服器,被複制的為主伺服器,對主伺服器進行復制的是從伺服器

進行復制的主從伺服器雙方的資料庫將儲存相同的資料,即資料庫狀態一致,簡稱“一致”

15.1 舊版複製功能的實現

複製功能分為同步和命令傳播兩個操作

  • 同步操作作用於伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態

  • 命令傳播則作用於在主伺服器的資料庫狀態被修改,導致主從伺服器的資料庫狀態出現不一致時,讓主從伺服器的資料庫重新回到一致狀態

15.1.1 同步

客戶端向從伺服器傳送 SLAVEOF 命令複製主伺服器時,首先從伺服器執行的是同步操作,更新至主伺服器當前所處的資料庫狀態

實際操作是從伺服器向主伺服器傳送 SYNC 命令:

  1. 從伺服器向主伺服器傳送 SYNC 命令
  2. 收到 SYNC 命令的主伺服器執行 BGSAVE 命令,在後臺生成一個 RDB 檔案,並使用一個緩衝區記錄從現在開始執行的所有寫命令
  3. 當主伺服器的 BGSAVE 命令執行完,主伺服器會將 BGSAVE 命令生成的 RDB 檔案傳送給從伺服器,從伺服器收到後載入 RDB 檔案,更新至主伺服器執行 BGSAVE 的狀態
  4. 主伺服器將記錄在緩衝區裡的所有寫命令傳送給從伺服器,從伺服器執行寫命令,更新指伺服器所處的狀態

15.1.2 命令傳播

同步操作之後,主從伺服器兩者的資料庫狀態將達到一致狀態,但當主伺服器執行客戶端的命令時,主伺服器的狀態可能修改,不再一致

為了讓主從伺服器再一次回到一致狀態,主伺服器需要對從伺服器執行命令傳播操作:

將造成主從伺服器不一致的寫命令傳送給從伺服器執行,執行後再一次回到一致狀態

15.2 舊版複製功能的缺陷

從伺服器對主伺服器的複製可以分為以下兩種情況:

  • 初次複製:從伺服器沒有複製過任何主伺服器,或者從伺服器當前要複製的主伺服器和上一次複製的主伺服器不同
  • 斷線後重複製:處於命令傳播階段的主從伺服器因網路原因中斷了複製,但從伺服器通風自動重連線重新連上了主伺服器,並繼續複製主伺服器

初次複製使用舊版複製功能你那個很好地完成任務,但是對於斷線後重複製來說,舊版複製功能效率低

在命令傳播階段某個時間點斷線後主伺服器執行了部分命令修改了資料庫狀態,而從伺服器還在重連,成功後是執行 SYNC 命令重新開始操作,但是實際上不是很需要的,因為主從伺服器在斷線之前是一致狀態的

SYNC 命令

  1. 主伺服器執行 BGSAVE 命令生成 RDB 檔案,耗費主伺服器 CPU、記憶體、磁碟 I/O 資源
  2. 傳送 RDB 檔案耗費主從伺服器大量的網路資源(頻寬和流量),並對主伺服器響應命令請求時間產生影響
  3. 從伺服器需要載入 RDB 檔案,進入阻塞狀態無法處理命令請求

15.3 新版複製功能的實現

使用 PSYNC 命令代替 SYNC 命令解決斷線重複制的低效問題,

PSYNC 命令有完整重同步和部分重同步兩種模式:

  • 完整重同步:初次複製情況,步驟和 SYNC 命令執行步驟一致
  • 部分重同步:斷線後重複製情況,主伺服器將斷線期間執行的寫命令傳送給從伺服器,從伺服器接收並執行,更新至伺服器當前狀態

15.4 部分重同步的實現

部分重同步功能由以下三個部分構成:

  • 主伺服器的複製偏移量和從伺服器的複製偏移量
  • 主伺服器的複製積壓緩衝區
  • 伺服器的執行 ID

15.4.1 複製偏移量

主伺服器和從伺服器會分別維護一個複製偏移量:

  • 主伺服器每次向從伺服器傳播 N 個位元組的資料時,就將自己的複製偏移量的值加上 N
  • 從伺服器每次收到主伺服器傳播的 N 個位元組資料時,就將自己的複製偏移量加上 N

通過對比偏移量可以知道主從伺服器是否處於一致狀態

15.4.2 複製積壓緩衝區

複製積壓緩衝區是由主伺服器維護的一個固定長度先進先出佇列,預設為 1 MB

當主伺服器進行命令傳播時,不僅會將寫命令發給所有從伺服器,還會將寫命令入隊到複製積壓緩衝區裡面,且佇列中每個位元組記錄相應的複製偏移量

當從伺服器重新連上主伺服器後,從伺服器會通過 PSYNC 命令將自己發覆制偏移量 offset 傳送給主伺服器,主伺服器根據此來決定對伺服器執行何種同步操作:

  • 如果 offset 之後的資料仍在複製積壓緩衝區中,則進行部分重同步操作
  • 相反,已經不存在複製緩衝區中則進行完整重同步操作

根據需要調整複製緩衝區大小

預設為 1 MB,最小跟由公式 second * write_size_per_second估算

  • second:伺服器斷線後重新連上主伺服器的平均時間,單位秒
  • write_size_per_second:伺服器平均每秒產生的寫命令資料量(協議格式的寫命令的長度總和)

安全起見,實際大小可以設定為:2 * second * write_size_per_second,可以保證絕大部分斷線情況都能用部分重同步處理

15.4.3 伺服器執行 ID

  • 每個伺服器都有自己的執行 ID
  • 執行 ID 在伺服器啟動時自動生成,由 40 個隨機的十六進位制字元組成

當從伺服器進行初次複製時,主伺服器會將自己的執行 ID 傳送給從伺服器,而從伺服器會將其儲存,當從伺服器斷線並重新連上一個主伺服器時,從伺服器將當前連線的主伺服器傳送之前儲存的執行ID:

  • 如果 ID 與當前連線的主伺服器 ID 相同,則說明斷線之前是這個主伺服器與之連線,則繼續嘗試部分重同步操作
  • 否則則不同,斷線之前不是這個主伺服器,執行完整重同步操作

15.5 PSYNC 命令的實現

PSYNC 命令的呼叫方法有兩種:

  • 如果從伺服器沒有複製過任何主伺服器,或者之前執行了 SLAVEOF no one 命令,則在開始一次新的複製時向主伺服器傳送 PSYNC ? -1 命令,主動請求主伺服器進行完整重同步
  • 否則傳送 PSYNC 命令
    • runid:上一次複製的主伺服器 ID
    • offset:從伺服器當前複製偏移量
    • 伺服器根據這兩個引數決定對從伺服器執行什麼同步操作

伺服器會返回以下三種回覆之一:

  • + FULLRESYNC 回覆,主伺服器將與從伺服器進行完整重同步操作
    • runid:主伺服器 ID,從伺服器儲存並作為下一個 PSYNC 引數使用
    • offset:主伺服器當前複製偏移量,從伺服器為將其作為自己的初始化偏移量
  • + CONTINUE 回覆:主伺服器將與從伺服器執行部分重同步操作
  • - ERR 回覆,主伺服器版本低於 2.8 識別不了 PSYNC 命令,需要從伺服器傳送 SYNC 命令,執行完整同步操作

15.6 複製的實現

SLAVEOF <master_ip> <master_port> 

15.6.1 步驟1:設定主伺服器的地址和埠

從伺服器將客戶端輸入的主伺服器的 IP 地址和埠儲存到伺服器狀態的 masterhost 和 masterport 屬性中

SLAVEOF 是一個非同步命令,完成以上兩個屬性的設定之後,從伺服器向客戶端返回 OK,表示複製指令已經被接收,但實際的複製工作將在 OK 之後才真正執行

15.6.2 步驟2:建立套接字連線

根據 IP 地址和埠,從伺服器建立連向主伺服器的套接字連線,如果連線成功,從伺服器將為此套接字關聯一個專門用於處理複製工作的檔案事件處理器,負責後續的工作

主伺服器在接收從伺服器的套接字之後為該套接字建立相應的客戶端狀態,並將從伺服器看作一個連線到主伺服器的客戶端看待,此時從伺服器將具有伺服器和客戶端兩種狀態:從伺服器可以向主伺服器傳送命令請求,而主伺服器則會向從伺服器返回命令回覆

15.6.3 步驟3:傳送 PING 命令

從伺服器成為客戶端後第一件事是向主伺服器傳送一個 PING 命令

  • 檢查套接字的讀寫狀態是否異常
  • 檢查主伺服器能否正常處理命令請求

從伺服器將遇到三種情況:

  • 主伺服器返回命令回覆,但從伺服器不能在規定時間內讀取命令回覆的內容,則表示主從伺服器之間的網路連線狀態不穩定,需要從伺服器斷開並重新建立套接字
  • 主伺服器返回錯誤。表示主伺服器無法處理從伺服器命令請求,則從伺服器斷開並重新建立套接字
  • 從伺服器讀到 “PONG” 回覆,表示主從伺服器狀態正常,可以進行後續操作

15.6.4 步驟4:身份驗證

下一步則是決定是否身份驗證:根據是否設定了 masterauth 選項

身份驗證:

從伺服器向主伺服器傳送了一條 AUTH 命令,引數為從伺服器 masterauth 選項的值

可能遇到幾種情況:

  • 主伺服器沒有設定 requirepass 選項,從伺服器也沒有設定 masetrauth 選項,主伺服器繼續執行從伺服器傳送的命令
  • 從伺服器通過 AUTH 傳送的密碼和主伺服器 requirepass 選項設定的密碼相同,繼續執行,否則則返回 invalid password 錯誤
  • 主伺服器有 requirepass 選項,從伺服器無 masetrauth 選項,主伺服器返回 NOAUTH 選項
  • 主伺服器無 requirepass 選項,從伺服器有 masetrauth 選項,主伺服器返回 no password is set 錯誤

錯誤情況令從伺服器中止目前的複製工作,並從建立套接字重新開始,直到身份驗證或者從伺服器放棄執行復制

15.6.5 步驟5:傳送埠資訊

從伺服器執行 REPLCONF listening-port ,向主伺服器傳送從伺服器的監聽埠號

主伺服器接收後記錄在從伺服器對應的客戶端狀態的 slave_listening_port 中,其唯一作用就是主伺服器執行 INFO replication 時列印從伺服器埠

15.6.6 步驟6:同步

從伺服器向主伺服器傳送 PSYNC 命令,執行同步操作,並將自己的資料庫更新至主伺服器資料庫當前所處的狀態,執行之後主伺服器也會成為從伺服器的客戶端

  • 如果是完整重同步,將儲存在緩衝區裡面的寫命令傳送給從伺服器執行
  • 如果是部分重同步,傳送儲存在複製緩衝區

15.6.7 步驟7:命令傳播

15.7 心跳檢測

命令傳播階段,從伺服器預設以每秒一次頻率向伺服器傳送命令:

REPLCONF ACK <replication_offset>

replication_offset:從伺服器當前的複製偏移量

三個作用:

  • 檢測主從伺服器的網路連線狀態
  • 輔助實現 min-slaves 選項
  • 檢測命令丟失

15.7.1 檢測主從伺服器的網路連線狀態

通過接受命令來檢查網路連線是否正常,如果沒有收到則連接出現問題

15.7.2 輔助實現 min-slaves 配置選項

min-slaves-to-write 和 min-slaves-max-lag 可以防止主伺服器在不安全的情況下執行寫命令

15.7.3 檢測命令丟失

根據兩個 REPLCONF ACK 命令可以得出從伺服器的寫命令是否在半路丟失,從複製偏移量的比較得出