《Redis設計與實現》學習筆記
多機資料庫的實現 - 複製
通過SLAVEOF命令或者設定slaveof選項,讓一個伺服器(從伺服器)去複製另一個伺服器(主伺服器),達到“資料庫狀態一致”效果。
舊版複製(Redis 2.8版本之前)
功能實現
同步(sync)
- 從伺服器向主伺服器傳送SYNC命令。
- 主伺服器收到SYNC命令後執行BGSAVE命令,建立一個子執行緒生成RDB檔案,並使用一個緩衝區記錄現在開始執行的所有寫命令。
- 主伺服器的BGSAVE命令執行完畢後,主伺服器將RDB檔案發給從伺服器,從伺服器接收並載入這個RDB檔案,將自己的資料庫狀態更新至主伺服器執行BGSAVE命令時的資料庫狀態。
- 主伺服器將記錄在緩衝區裡面的所有寫命令傳送給從伺服器,從伺服器執行這些寫命令,將自己的資料庫狀態更新至主伺服器資料庫當前所處的狀態。
命令傳播(command propagate)
同步後主伺服器資料庫被修改,為了讓主從伺服器保持一致狀態,主伺服器將自己執行的寫命令傳送給從伺服器執行相同的寫命令。
缺陷
複製可分為初次複製和斷線後重複製
斷線後重複製每次都需要執行一次SYNC命令,重新複製所有的資料庫資料,非常低效。
新版複製
區別
Redis從2.8版本開始,使用PSYNC命令代替SYNC命令來執行復制時的同步問題。
PSYNC命令
PSYNC命令具有完整重同步和部分重同步兩種模式
完整重同步
用於處理初次複製情況,和SYNC初次複製基本一致。
部分重同步
用於處理斷線後重複製情況,從伺服器斷線重連後,在一定條件下,主伺服器可以將斷開期間執行的寫命令傳送給從伺服器。
部分重同步的實現
部分重同步功能由以下三個部分構成:
- 主從伺服器的複製偏移量(replication offset)
- 主伺服器的複製積壓緩衝區(replication backlog)
- 伺服器的執行ID(run ID)
1. 複製偏移量
主伺服器每次向從伺服器傳播N個位元組資料,就將自己的複製偏移量的值加上N;
從伺服器每次接收主伺服器傳播來的N個位元組資料,就將自己的複製偏移量的值加N。
通過對比主從伺服器的偏移量
2. 複製積壓緩衝區
複製積壓緩衝區是由主伺服器維護的一個固定長度(fixed_size)先進先出(FIFO)佇列,預設大小為1MB。
主伺服器在進行命令傳播時,不僅會將寫命令傳送給從伺服器,還會將寫命令入隊到複製積壓緩衝區,複製積壓緩衝區會為佇列中的每個位元組記錄相應的複製偏移量。
從伺服器斷開重連後會通過PSYNC命令將自己的複製偏移量offset傳送給主伺服器,主伺服器根據這個複製偏移量決定執行何種同步操作。
-
offset偏移量之後的資料仍存在於複製積壓緩衝區,則執行部分重同步;
-
offset偏移量之後的資料不存在於複製積壓緩衝區,則執行完整重同步。
複製積壓緩衝區的大小可以根據公式second * write_size_per_second來估算
secoond為從伺服器斷線重連所需的平均時間(以秒計算)
write_size_per_second為主伺服器平均每秒產生的寫命令資料量
一般修改配置檔案中repl-backlog-size選項將複製積壓緩衝區大小設為
2 * second * write_size_per_second
3. 伺服器執行ID
每個Redis伺服器有自己的執行ID,執行ID在伺服器啟動時自動生成,由40個隨機的十六進位制字元組成。
從伺服器對主伺服器進行初次複製時,主伺服器會將自己的執行ID傳送給從伺服器,從伺服器會將這個執行ID儲存起來。
當從伺服器斷線重連上一個主伺服器時,從伺服器會向當前連線的主伺服器傳送之前儲存的執行ID。
- 從伺服器儲存的執行ID和當前連線的主伺服器執行ID相同,則主伺服器會嘗試部分重同步操作;
- 從伺服器儲存的執行ID和當前連線的主伺服器執行ID不相同,則主伺服器執行完整同步操作。
PSYNC命令的實現
從伺服器:
-
如果從伺服器沒有複製過任何主伺服器或者執行過SLAVEOF no one命 令,則從伺服器向主伺服器傳送PSYNC ? -1命令。請求完整重同步。
-
如果從伺服器複製過某個主伺服器,則從伺服器向主伺服器傳送
PSYNC <runid> <offset>命令,主伺服器根據兩個引數判斷執行哪種同步操作。
主伺服器:
- 主伺服器返回+FULLRESYNC <runid> <offset>回覆,表示執行完整重同步操作,其中runid和offset分別是主伺服器的執行ID和複製偏移量,用於初始化從伺服器。
- 主伺服器返回+CONTINUE回覆,表示執行部分重同步。
- 主伺服器返回+ERR回覆,表示主伺服器版本低於Redis 2.8,識別不了PSYNC命令,從伺服器會向主伺服器傳送SYNC命令,執行完整重同步。A
複製的實現 (七步驟)
- 設定主伺服器的地址和埠
通過SLAVEOF <master_ip> <master_port>,設定主從伺服器
從伺服器將主伺服器的IP地址及埠儲存到伺服器狀態的masterhost屬性和masterport屬性。
- 建立套接字連線
從伺服器根據命令中的IP地址和埠,建立連向主伺服器的套接字連線
如果成功連線,從伺服器為該套接字關聯一個專門用於處理複製工作的檔案事件處理器,如接受RDB檔案,接受主伺服器傳來的寫命令等。
如果接受連線,主伺服器為該套接字建立相應的客戶端狀態,這時候從伺服器同時具有伺服器和客戶端兩個身份。
- 傳送PING命令
從伺服器成為主伺服器的客戶端之後,第一件事就是傳送一個PING命令。
PING命令的作用:
- 檢查套接字的讀寫狀態是否正常
- 檢查主伺服器能否正常處理命令請求,為後續步驟做檢查
PING命令返回情況:
主伺服器返回了一個命令回覆,但從伺服器不能在規定時限(timeout)內讀取出命令回覆內容,表示主從伺服器之間的網路連線狀態不佳,無法繼續執行後續步驟。
(從伺服器斷開並重新建立連向主伺服器的套接字)
主伺服器返回了一個錯誤,表示主伺服器暫時沒法處理從伺服器的命令請求,無法繼續執行後續步驟。
(從伺服器斷開並重新建立連向主伺服器的套接字)
從伺服器讀取到“pong”回覆,表示主從伺服器之間的網路連線狀態正常,主伺服器能正常處理命令請求,可以繼續執行後續步驟。
- 身份驗證
根據從伺服器是否設定了masterauth選項,決定是否進行身份驗證。
設定了masterauth:
從伺服器向主伺服器傳送一條AUTH命令,命令引數為從伺服器masterauth選項的值
身份驗證的三種情況:
主伺服器沒有設定requirepass選項,從伺服器也沒有設定masterauth選項,主伺服器繼續執行從伺服器傳送的命令,複製工作繼續進行。
從伺服器通過AUTH命令傳送的密碼和主伺服器requirepass選項設定的密碼相同,主伺服器繼續執行從伺服器傳送的命令,複製工作繼續進行。如果不相同,主伺服器返回一個invalid password錯誤。
如果主伺服器設定了requirepass選項,從伺服器沒有設定masterauth選項,主伺服器返回一個NOAUTH錯誤;
如果主伺服器沒有設定requirepass選項,從伺服器設定了masterauth選項,主伺服器返回一個no password is set錯誤。
- 傳送埠資訊
從伺服器執行命令REPLCONF listening-port <port-number>,向主伺服器傳送從伺服器的監聽埠號。
主伺服器將埠號記錄在從伺服器對應的客戶端狀態的slave_listening_port屬性中,該屬性目前唯一的作用是在主伺服器執行INFO replication命令時列印從伺服器的埠號。
- 同步
從伺服器向主伺服器傳送PSYNC命令,執行同步操作
執行同步操作前,只有從伺服器是主伺服器的客戶端,執行同步操作後,主從伺服器互為對方的客戶端
(只有成為客戶端,才可以互相向對方傳送命令請求或者返回命令回覆)
- 命令傳播
主伺服器一直將執行的寫命令傳送給從伺服器,從伺服器一直接收並執行主伺服器發來的寫命令,保持主從一致。
心跳檢測
命令傳播階段,從伺服器每秒一次向主伺服器傳送命令:
REPLCONF ACK <replication_offset>,其中replication_offset為從伺服器當前的複製偏移量。
REPLCONF ACK命令的作用
- 檢測主從伺服器的網路連線狀態
主伺服器超過一秒鐘沒有收到從伺服器發來的REPLCONF ACK命令,則說明主從伺服器之間的連接出現問題。
可以通過INFO replication命令,在伺服器列表的lag屬性檢視從伺服器上一次傳送REPLCONF ACK距離現在的時間。(一般在0~1秒之間跳動)
- 輔助實現min-slaves選項
Redis中的min-slaves-to-write和min-slaves-max-lag可以防止主伺服器在不安全的情況下執行寫命令,這裡的min-slaves-max-lag就是上面提到的讀取的lag值。
- 檢測命令丟失
從伺服器在傳送REPLCONF ACK命令時傳送複製偏移量,如果因為網路故障發生命令丟失,主從伺服器複製偏移量不同,主伺服器會根據從伺服器提交的複製偏移量,在複製積壓緩衝區找到丟失的資料重新發送。
(不同於部分重同步,區別是有沒有斷線)