Redis 的復制功能分為同步(sync)和命令傳播(command propagate)兩個操作:
* 其中, 同步操作用于將從服務器的數據庫狀態更新至主服務器當前所處的數據庫狀態。
* 而命令傳播操作則用于在主服務器的數據庫狀態被修改, 導致主從服務器的數據庫狀態出現不一致時, 讓主從服務器的數據庫重新回到一致狀態。
本節接下來將對同步和命令傳播兩個操作進行詳細的介紹。
## 同步
當客戶端向從服務器發送?SLAVEOF?命令, 要求從服務器復制主服務器時, 從服務器首先需要執行同步操作, 也即是, 將從服務器的數據庫狀態更新至主服務器當前所處的數據庫狀態。
從服務器對主服務器的同步操作需要通過向主服務器發送?SYNC?命令來完成, 以下是?SYNC?命令的執行步驟:
1. 從服務器向主服務器發送?SYNC?命令。
2. 收到?SYNC?命令的主服務器執行?BGSAVE?命令, 在后臺生成一個 RDB 文件, 并使用一個緩沖區記錄從現在開始執行的所有寫命令。
3. 當主服務器的?BGSAVE?命令執行完畢時, 主服務器會將?BGSAVE?命令生成的 RDB 文件發送給從服務器, 從服務器接收并載入這個 RDB 文件, 將自己的數據庫狀態更新至主服務器執行?BGSAVE?命令時的數據庫狀態。
4. 主服務器將記錄在緩沖區里面的所有寫命令發送給從服務器, 從服務器執行這些寫命令, 將自己的數據庫狀態更新至主服務器數據庫當前所處的狀態。
圖 IMAGE_SYNC 展示了?SYNC?命令執行期間, 主從服務器的通信過程。

表 TABLE_SYNC_EXAMPLE 展示了一個主從服務器進行同步的例子。
| 時間 | 主服務器 | 從服務器 |
| --- | --- | --- |
| T0 | 服務器啟動。 | 服務器啟動。 |
| T1 | 執行?`SET?k1?v1`?。 | ? |
| T2 | 執行?`SET?k2?v2`?。 | ? |
| T3 | 執行?`SET?k3?v3`?。 | ? |
| T4 | ? | 向主服務器發送?SYNC?命令。 |
| T5 | 接收到從服務器發來的?SYNC?命令, 執行?BGSAVE?命令, 創建包含鍵?`k1`?、?`k2`?、?`k3`?的 RDB 文件, 并使用緩沖區記錄接下來執行的所有寫命令。 | ? |
| T6 | 執行?`SET?k4?v4`?, 并將這個命令記錄到緩沖區里面。 | ? |
| T7 | 執行?`SET?k5?v5`?, 并將這個命令記錄到緩沖區里面。 | ? |
| T8 | BGSAVE?命令執行完畢, 向從服務器發送 RDB 文件。 | ? |
| T9 | ? | 接收并載入主服務器發來的 RDB 文件 , 獲得?`k1`?、?`k2`?、?`k3`?三個鍵。 |
| T10 | 向從服務器發送緩沖區中保存的寫命令?`SET?k4?v4`?和?`SET?k5v5`?。 | ? |
| T11 | ? | 接收并執行主服務器發來的兩個?SET?命令, 得到?`k4`?和?`k5`?兩個鍵。 |
| T12 | 同步完成, 現在主從服務器兩者的數據庫都包含了鍵?`k1`?、`k2`?、?`k3`?、?`k4`?和?`k5`?。 | 同步完成, 現在主從服務器兩者的數據庫都包含了鍵?`k1`?、?`k2`?、`k3`?、?`k4`?和?`k5`?。 |
## 命令傳播
在同步操作執行完畢之后, 主從服務器兩者的數據庫將達到一致狀態, 但這種一致并不是一成不變的 —— 每當主服務器執行客戶端發送的寫命令時, 主服務器的數據庫就有可能會被修改, 并導致主從服務器狀態不再一致。
舉個例子, 假設一個主服務器和一個從服務器剛剛完成同步操作, 它們的數據庫都保存了相同的五個鍵?`k1`?至?`k5`?, 如圖 IMAGE_CONSISTENT 所示。

如果這時, 客戶端向主服務器發送命令?`DEL?k3`?, 那么主服務器在執行完這個?DEL?命令之后, 主從服務器的數據庫將出現不一致: 主服務器的數據庫已經不再包含鍵?`k3`?, 但這個鍵卻仍然包含在從服務器的數據庫里面, 如圖 IMAGE_INCONSISTENT 所示。

為了讓主從服務器再次回到一致狀態, 主服務器需要對從服務器執行命令傳播操作: 主服務器會將自己執行的寫命令 —— 也即是造成主從服務器不一致的那條寫命令 —— 發送給從服務器執行, 當從服務器執行了相同的寫命令之后, 主從服務器將再次回到一致狀態。
在上面的例子中, 主服務器因為執行了命令?`DEL?k3`?而導致主從服務器不一致, 所以主服務器將向從服務器發送相同的命令?`DEL?k3`?: 當從服務器執行完這個命令之后, 主從服務器將再次回到一致狀態 —— 現在主從服務器兩者的數據庫都不再包含鍵?`k3`?了, 如圖 IMAGE_PROPAGATE_DEL_k3 所示。

- 介紹
- 前言
- 致謝
- 簡介
- 第一部分:數據結構與對象
- 簡單動態字符串
- SDS 的定義
- SDS 與 C 字符串的區別
- SDS API
- 重點回顧
- 參考資料
- 鏈表
- 鏈表和鏈表節點的實現
- 鏈表和鏈表節點的 API
- 重點回顧
- 字典
- 字典的實現
- 哈希算法
- 解決鍵沖突
- rehash
- 漸進式 rehash
- 字典 API
- 重點回顧
- 跳躍表
- 跳躍表的實現
- 跳躍表 API
- 重點回顧
- 整數集合
- 整數集合的實現
- 升級
- 升級的好處
- 降級
- 整數集合 API
- 重點回顧
- 壓縮列表
- 壓縮列表的構成
- 壓縮列表節點的構成
- 連鎖更新
- 壓縮列表 API
- 重點回顧
- 對象
- 對象的類型與編碼
- 字符串對象
- 列表對象
- 哈希對象
- 集合對象
- 有序集合對象
- 類型檢查與命令多態
- 內存回收
- 對象共享
- 對象的空轉時長
- 重點回顧
- 第二部分:單機數據庫的實現
- 數據庫
- 數據庫鍵空間
- 重點回顧
- RDB 持久化
- RDB 文件結構
- 重點回顧
- AOF 持久化
- AOF 持久化的實現
- 重點回顧
- 事件
- 文件事件
- 重點回顧
- 參考資料
- 客戶端
- 客戶端屬性
- 重點回顧
- 服務器
- 命令請求的執行過程
- 重點回顧
- 第三部分:多機數據庫的實現
- 復制
- 舊版復制功能的實現
- 重點回顧
- Sentinel
- 啟動并初始化 Sentinel
- 重點回顧
- 參考資料
- 集群
- 節點
- 重點回顧
- 第四部分:獨立功能的實現
- 發布與訂閱
- 頻道的訂閱與退訂
- 重點回顧
- 參考資料
- 事務
- 事務的實現
- 重點回顧
- Lua 腳本
- 創建并修改 Lua 環境
- 重點回顧
- 排序
- SORT <key> 命令的實現
- 重點回顧
- 二進制位數組
- GETBIT 命令的實現
- 重點回顧
- 慢查詢日志
- 慢查詢記錄的保存
- 慢查詢日志的閱覽和刪除
- 添加新日志
- 重點回顧
- 監視器
- 成為監視器
- 向監視器發送命令信息
- 重點回顧
- 源碼、相關資源和勘誤