一. 概述 使用和配置主從複製非常簡單,每次當 slave 和 master 之間的連接斷開時, slave 會自動重連到 master 上,並且無論這期間 master 發生了什麼, slave 都將嘗試讓自身成為 master 的精確副本。這個系統的運行依靠三個主要的機制: (1) 當一個 ma ...
一. 概述
使用和配置主從複製非常簡單,每次當 slave 和 master 之間的連接斷開時, slave 會自動重連到 master 上,並且無論這期間 master 發生了什麼, slave 都將嘗試讓自身成為 master 的精確副本。這個系統的運行依靠三個主要的機制:
(1) 當一個 master 實例和一個 slave 實例連接正常時, master 會發送一連串的命令流來保持對 slave 的更新,以便於將自身(master)數據集的改變複製給 slave 。包括客戶端的寫入、key 的過期或master庫的其它改變動作。
(2) 在Redis2.6之前,主從斷開重連後,會進行一次快照操作(rdb)然後將快照發送給從資料庫,即使斷開期間只有幾條命令被執行,這就使得斷開重聯後的數據恢復過程效率很低。Redis2.8之後,當 master 和 slave 之間的連接斷開之後(因為網路問題、主庫或從庫檢測到超時), slave 會重新連接上 master 並嘗試進行"部分重同步"也叫增量複製(partial resynchronization),用於獲取在斷開連接期間內丟失的命令流。
(3) 當無法進行部分重同步時,slave 會請求進行"完整同步"(full resynchronization)。這會涉及到一個更複雜的過程,例如 master 需要創建所有數據的快照,將之發送給 slave ,之後在master數據集更改時持續發送命令流到 slave。也就是2.8後有了全部同步和部分重同步兩種模式,而2.6之前只有全部同步模式。
二. 複製特點
Redis使用預設的非同步複製,具有低延遲和高性能,是絕大多數Redis使用的複製模式。從庫會非同步的確認與主庫定期接收到的數據量。主庫不會每次去等待從庫處理完命令。類似mysql半同步複製功能可以通過min-slaves配置選項來輔助。下麵介紹Redis複製的一些特點:
(1) Redis使用非同步複製,slave 和 master 之間非同步地確認處理的數據量。
(2) 一個 master 可以擁有多個 slave。
(3) slave 可以接受其他 slave 的連接。除了多個 slave 可以連接到同一個 master 之外, slave 之間也可以像層疊狀的結構(cascading-like structure)連接到其他 slave 。自Redis 4.0 版本起所有的 sub-slave 將會從 master 收到完全一樣的複製流。
(4) Redis複製在 master這邊是非阻塞的。這意味著 master 在一個或多個 slave 進行初次同步或者是部分重同步時,可以繼續處理查詢請求。
(5) 複製在 slave 這邊大部分也是非阻塞的。當 slave 進行初次同步時,它可以使用舊數據集處理查詢請求,假設你在redis.conf中配置了讓Redis這樣做的話。
(6) 複製既可以被用在可伸縮性,以便只讀查詢可以有多個 slave 進行(例如 O(N) 複雜度的慢操作可以被下放到 slave ),或者僅用於數據安全。
(7) 可以使用複製來避免 master 將全部數據集寫入磁碟造成的開銷:一種典型的技術是配置你的 master Redis.conf以避免對磁碟進行持久化,然後連接一個 slave ,其配置為不定期保存或是啟用 AOF。但是,這個設置必須小心處理,因為重新啟動的 master 程式將從一個空數據集開始:如果一個 slave 試圖與它同步,那麼這個 slave 也會被清空。
(8) 在使用Redis複製功能時,強烈建議在 master 和在 slave 中啟用持久化。假如:持久化被關閉了,Redis主節點重啟後其數據集合為空。而其它slave從節點會複製主節點數據,在複製時會銷毀自身之前的數據副本。這樣下來複制構架的所有節點數據都會被清空。
三 Redis複製同步原理
3.1 全部同步的實現
當客戶端向從伺服器發送slaveof命令,要求從伺服器複製主伺服器時,從伺服器首先需要執行同步操作,將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀,過程如下:
(1) 從伺服器向主伺服器發送psync命令。
(2) 收到psync命令的主伺服器執行BGSAVE命令,開啟一個後臺子進程生成一個 RDB 快照文件,並同時使用一個緩衝區記錄從現在執行的所有寫命令。
(3) 當主伺服器的BGSAVE命令執行完,主伺服器會將生成的RDB文件發送給從伺服器,從伺服器接收並載入這個RDB文件,然後載入文件到記憶體。將自己的資料庫狀態更新至主伺服器執行BGSAVE命令時的資料庫狀態。
(4) 主伺服器將記錄在緩衝區裡面的所有寫命令發送給從伺服器,從伺服器執行這些寫命令,將自己的資料庫狀態更新至主伺服器資料庫當前所處狀態。這個過程以指令流的形式完成並且和Redis協議本身的格式相同。
下麵是使用psync命令進行全部同步的過程:
時間 |
主伺服器 |
從伺服器 |
T0 |
伺服器啟動 |
伺服器啟動 |
T1 |
執行set k1 v1 |
|
T2 |
執行set k2 v2 |
|
T4 |
.. |
向主伺服器發送psync命令 |
T5 |
接收到從伺服器的psync命令,執行bgsave命令,創建RDB文件,並使用緩衝區記錄接下來執行所有寫命令. |
|
T6 |
執行set k3 v3,將這個命令記錄在緩衝區中 |
|
T7 |
執行set k4 v4,將這個命令記錄在緩衝區中 |
|
T8 |
Bgsave執行完成,向從伺服器發送RDB文件 |
|
T9 |
|
接收載入RDB文件 |
T10 |
向從伺服器發送緩衝區中保存的寫命令 |
|
T11 |
|
接收緩衝區中的寫命令,執行 |
T12 |
同步完成 |
同步完成 |
3.2 部分重同步的實現
在主從伺服器斷開後,從伺服器會嘗試不斷的發送psync命令,使用部分重同步模式,讓主從伺服器重新回到一致狀態。使用部分重同步主伺服器只需要將從伺服器缺少的寫命令發送給從伺服器,從伺服器執行就可以了。功能由三個部份構成:
(1) 主伺服器的複製偏移量(replication offset)和從伺服器的複製偏移量。
(2)主伺服器的複製積壓緩衝區(replication backlog)。
(3) 伺服器的運行ID (run ID) 。
3.2.1 複製偏移量:主伺服器每次向從伺服器傳播N個位元組的數據時,就將自己的複製偏移量的值加上N。從伺服器每次接收到主伺服器傳播來的N個位元組的數據時,就將自己的複製偏移量的值加上N。
例如:主伺服器向從伺服器傳播長度為33個位元組數據,那麼主伺服器的複製偏移量將更新為10086+33=10119,而從伺服器在接收到主伺服器傳播的數據之後,也會將複製偏移量更新為10119。如果主從偏移量相同,表示主從伺服器處於一致狀態,反之則不一致。
3.2.2 複製積壓緩衝區: 該緩衝區是由主伺服器維護一個固定長度先進先出的隊列,預設大小為1MB。當主伺服器進行命傳播時,它不僅會將寫命令發送給所有從伺服器,還會將寫命令入隊列到複製積壓緩衝區中。因此主伺服器的複製積壓緩衝區中會保存著一部分最近傳播的寫命令,並且複製積壓緩衝區會為隊列中的每個位元組記錄相應的複製偏移量。
當從伺服器重新連上主伺服器時,從伺服器會通過psync命令將自己的複製偏移量offset發送給主伺服器,主伺服器會根據這個複製偏移量來決定從伺服器執行何種同步操作:
(1) 如果從伺服器發送的offest偏移量之後的數據仍然存在於複製積壓緩衝區中,那麼主伺服器將對從伺服器執行部分重同步操作。
(2) 如果從伺服器發送的如果offest偏移量之後的數據已經不存在於複製積壓緩衝區中,那麼主服器將對從伺服器執行完整同步操作。
註意:緩衝區預設大小為1MB,如果主伺服器需要執行大量寫命令,又或者主從伺服器斷線後重連接所需的時間比較長,那麼這個大小也許並不合適。查看積壓緩衝區大小(單位位元組)如下:
127.0.0.1:6379> config get repl-backlog-size 1) "repl-backlog-size" 2) "1048576"
3.2.3 伺服器運行ID:每個redis伺服器,不論主從都有自己的運行ID。運行ID在伺服器啟動時自動生成,由40個隨機的十六進位字元組成。當從伺服器對主伺服器進行初次複製時,主伺服器會將自已運行的ID傳送給從伺服器,從伺服器會將這個運行ID保存起來。當從伺服器斷開重連到一個主伺服器時,從伺服器將向當前連接的主伺服器發送之前保存的運行ID
(1) 如果從伺服器保存的運行ID和當前連接的主伺服器的運行ID相同,那麼說明從伺服器斷開後重連的還是之前的主伺服器,主伺服器將繼續嘗試執行部分重同步操作。
(2) 反之,如果運行ID不相同,那說明從伺服器斷開後重連的不是之前的主伺服器,主伺服器將對從伺服器執行完整同步操作。
下麵使用psync命令來進行斷線後,重覆制的過程:
時間 |
主伺服器 |
從伺服器 |
T0 |
主從伺服器完成同步 |
主從伺服器完成同步 |
T1 |
執行並傳播set k1 v1 |
執行主伺服器傳來的set k1 v1 |
.. |
.. |
.. |
T10087 |
主從伺服器連接斷開 |
主從伺服器連接斷開 |
T10088 |
執行set k10087 v10087 |
斷線後,嘗試重新連接主伺服器 |
T10089 |
執行set k10088 v10088 |
斷線後,嘗試重新連接主伺服器 |
T10090 |
主從伺服器重新連接 |
主從伺服器重新連接 |
T10091 |
|
向主伺服器發送psync命令 |
T10092 |
向從伺服器返回+continue回覆,表示執行部分重同步 |
|
T10093 |
|
接收+continue回覆,表示執行部分重同步 |
T10094 |
主從伺服器再次完成同步 |
主從伺服器再次完成同步 |
四. psync命令的實現
4.1 PSYNC命令的調用方法有兩種:
(1) 如果從伺服器以前沒有複製過任何主伺服器,那麼從伺服器在開始一次新的複製時,將向主伺服器發送psync ? -1 命令,主動請求主伺服器進行完整同步。
(2) 相反,如果從伺服器已經複製過某個主伺服器,那麼從伺服器在開始一次新的複製時,將向主伺服器發送 psync runid offset 命令。通過兩個參數來判斷應該對伺服器執行哪種同步操作。
4.2 根據情況,主伺服器接收到psync命令返回以下三種回覆的其中一種:
(1) 如果主伺服器返回 +fullresync runid offset回覆,那麼表示主伺服器將與從伺服器執行“完整同步”操作。
(2) 如果主伺服器返回+continue回覆,那麼表示主伺服器將與從伺服器執行“部分重同步”操作。
(3) 如果主伺服器返回-err回覆,那麼表示主伺服器的版本低於redis 2.8,它識別不了psync命令,從伺服器將向主伺服器發送sync命令,並與主伺服器執行完整同步操作。
下圖是psync命令執行完整同步和部分重同步時可能遇到的情況: