轉自https://www.cnblogs.com/wind-snow/p/11396446.html
Redis 中,可以通過執行 savleof 命令或者設置 slaveof 選項,讓一個服務器去復制另一個服務器,我們稱被復制的服務器為主服務器,而對主服務器進行復制的服務器則被稱為從服務器。
命令示例: 127.0.0.1:12345>SLAVEOF 127.0.0.1 6379
Redis 2.8 之前復制功能的實現
Redis 中的復制分為同步和命令傳播兩個操作。
- 同步操作是將從服務器的數據庫狀態更新值主服務器當前所處的數據庫狀態。
- 命令傳播操作則用於在主服務器的數據庫狀態被修改,導致主從服務器的數據庫出現不一致時,讓主從服務器的數據庫重新回到一致狀態。
同步
當客戶端向從服務器發送 slaveof 命令,要求從服務器復制主服務器時,從服務器首先需要執行同步操作,也即是,將從服務器的數據庫狀態更新值主服務器當前所處的數據庫狀態。
從服務器對主服務器的同步操作是通過向主服務器發送 sync 命令來完成,執行步驟如下:
- 從服務器向主服務器發送 sync 命令。
- 收到 sync 命令的主服務器執行 bgsave 命令,在后台生成一個 RDB 文件,並用一個緩沖區記錄從現在開始執行的所有寫命令。
- 當主服務器 BGSAVE 命令執行完畢時,主服務器會將 BGSAVE 命令生成的 RDB 文件發送給從服務器,從服務器接收並載入這個 RDB 文件,將自己的數據庫狀態更新至主服務器執行 BGSAVE 命令時的數據庫狀態。
- 主服務器將記錄在緩沖區里面的所有寫命令發送給從服務器,從服務器執行這些寫命令,將自己的數據庫狀態更新至主服務器數據庫當前所處的狀態。
sync 命令執行期間,主從服務器的通信過程如下圖所示(出自《Redis設計與實現第二版》第十五章:復制):
命令傳播
命令傳播是指:在同步之后,主服務器會將同步期間緩沖區保存的寫命令,發送給從服務器執行,當從服務器執行了相同的寫命令之后,主從服務器將再次回到一致狀態。
Redis 2.8 之前復制功能的缺陷
在 Redis 中,從服務器對主服務器的復制分為兩種情況:
- 初次復制:從服務器以前沒有復制過主服務器。
- 斷線后重新復制:從服務器因為網絡原因中斷了復制,但從服務器通過自動重連接重新連接上了主服務器,並繼續復制主服務器。
對於初次復制來說,舊版復制功能能夠很好的完成任務,但對於斷線后重新復制來說,舊版復制功能雖然也能讓主從服務器數據一致(重新發起SYNC請求,主服務器重新生成RDB文件進行同步),但效率卻非常低(可能主從斷開連接過程中主服務器只執行了一條寫命令卻要重新生成整個RDB文件,耗費CPU,再傳輸給從服務器,耗費網絡帶寬)。
Redis 2.8 之后復制功能的實現
為了解決舊版復制功能在處理斷線重新復制情況下的低效問題,Redis 從 2.8 版本開始,使用 psync命令代替了 sync 命令來執行復制時的同步操作。
psync 命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)兩種模式。
- 完整重同步用於處理初次復制情況:完整步驟和 sync 命令的步驟基本一樣。
- 部分重同步則用於處理斷線后重新復制的情況:當從服務器重新連接主服務器時,如果條件允許,主服務器可以將從服務器連接斷開期間執行的寫命令發送給從服務器,從服務器只要接收並執行這些寫命令,就可以將數據庫數據更新至和主服務器一樣。
部分重同步的執行過程如下圖所示(出自《Redis設計與實現第二版》第十五章:復制):
部分重同步的實現
部分重同步功能包含三個部分:
- 主服務器的復制偏移量和從服務器的復制偏移量。
- 主服務器的復制積壓緩沖區。
- 服務器的運行 ID。
復制偏移量
執行復制的雙方——主服務器和從服務器會分別維護一個復制偏移量。
- 主服務器每次向從服務器傳播 N 個字節時,就將自己的復制偏移量 + N。
- 從服務器每次收到主服務器傳來的 N 個字節的數據時,就將自己的復制偏移量 + N。
對比主從服務器的復制偏移量可知:
- 如果主從服務器的復制偏移量相等,則說明主從服務器數據一致;
- 相反,如果主從服務器兩者的偏移量不等,則說明主從服務器中的數據不一致。
復制積壓緩沖區
復制積壓緩沖區是由主服務器維護的一個固定長度的先進先出(FIFO)隊列,默認大小為 1MB。
當主服務器進行命令傳播時,不僅會將寫命令發送給所有從服務器,還會將寫命令入隊到復制積壓緩沖區里面。
因此,主服務器的復制積壓緩沖區里面會保存着一部分最近傳播的寫命令,並且復制積壓緩沖區會為隊列中的每個字節記錄相應的復制偏移量。
當從服務器重新連上主服務器時,從服務器會通過 PSYNC 命令將自己的復制偏移量, offset 發送給主服務器,主服務器會根據這個復制偏移量來決定對從服務器執行何種同步操作:
- 如果 offset 偏移量之后的數據(也即是偏移量 offset + 1 開始的數據)仍然存在於復制積壓緩沖區里面,那么主服務器將對從服務器執行部分重同步操作。
- 相反,如果 offset 偏移量之后的數據已經不存在於復制積壓緩沖區,那么主服務器將對從服務器執行完整重同步操作。
注意:建議根據下述公式調整復制積壓緩沖區的大小為:2 * second * write_size_per_second
- 其中 second 為從服務器斷線后重新連接上主服務器所需的平均時間(單位秒)。
- write_size_per_second 是主服務器平均每秒產生的寫命令數據量。
- 對應redis-service.conf的配置項repl-backlog-size
服務器運行 ID
- 每個 Redis 服務器,不論主服務器還是從服務器都有自己的運行 ID。
- 運行 ID 在服務器啟動時自動生成,40個隨機十六進制字符。
當從服務器對主服務器進行初次復制時,主服務器會將自己的運行 ID 傳送給從服務器,而從服務器會將這個運行 ID 保存起來,
當從服務器斷線並重新連上主服務器時,從服務器將向當前連接的主服務器發送之前保存的運行 ID:
- 如果從服務器保存的運行 ID 和當前連接的主服務器的運行 ID 相同,那么說明從服務器斷線之前復制的就是當前連接的這個主服務器,主服務器可以繼續嘗試執行部分重同步操作。
- 如果從服務器保存的運行 ID 和當前連接的主服務器的運行 ID 不相同,那么說明從服務器斷線之前復制的主服務器不是當前連接的這個主服務器,主服務器將對從服務器執行完整重同步操作。
心跳檢測
在命令傳播階段,從服務器默認會以每秒一次的頻率,向主服務器發送命令:
REAPCONF ACK <replication_offset>
其中 replication_offset 是代表從服務器當前的復制偏移量。
發送 REAPCONF ACK 命令對於主從服務器有三個作用:
- 檢測主從服務器的網絡連接狀態。
- 輔助實現 min-slaves 選項。
- 檢測命令丟失。
檢測主從服務器的網絡連接狀態
主從服務器可以通過發送和接受 REAPCONF ACK 命令來檢查兩者之間的網絡連接是否正常:如果主服務器超過一秒沒有收到從服務器發來的 REAPCONF ACK 命令,那么主服務器就知道主從服務器的連接出現問題了。
輔助實現 min-slaves 選項
Redis 的 min-slaves-to-write 和 min-slaves-max-lag 兩個選項可以防止主服務器在不安全的情況下執行寫命令。
比如,如果我們向主服務器提供以下設置:
min-slaves-to-write 3 min-slaves-max-lag 10
那么在從服務器的數量少於3個或者3個從服務器的延遲(lag)值都大於或等於 10s 時,主服務器將拒絕執行寫命令。
檢測命令丟失
如果因為網絡故障,主服務器傳播給從服務器的寫命令在半路丟失,那么當從服務器向主服務器發送 REAPCONF ACK 命令時,主服務器將發覺從服務器當前的復制偏移量少於自己的復制偏移量,然后主服務器就會根據從服務器提交的復制偏移量,在復制積壓緩沖區里面找到從服務器缺少的數據,並將這些數據重新發送給從服務器。