前言
Redis 的主從復制和 MySQL 差不多,主要起着 數據備份,讀寫分離等作用。所以說主從復制對 Redis 來說非常重要,而無論是面試還是工作總,了解 「Redis 主從復制」 底層實現有非常有必要,那么接下來就和大家來看看 Redis 主從復制是怎么實現的吧。
什么是 Redis 主從復制?
在 Redis 中,我們可以通過 SLAVEOF 命令或者 slaveof 選項,讓一個服務器去復制另一個服務器,被復制的服務器稱為“「主服務器」”,發起復制的服務器稱為“「從服務器」”,由兩種服務器組成的模式稱為“「主從復制」”。

Redis 主從復制有以下特點:
-
Redis 使用異步復制,slave 和 master 之間異步地確認處理的數據量。
-
一個 master 可以擁有多個 slave。
-
slave 可以接受其他 slave 的連接。除了多個 slave 可以連接到同一個 master 之外, slave 之間也可以像層疊狀的結構(cascading-like structure)連接到其他 slave 。自 Redis 4.0 起,所有的 sub-slave 將會從 master 收到完全一樣的復制流。
-
Redis 復制在 master 側是非阻塞的。這意味着 master 在一個或多個 slave 進行初次同步或者是部分重同步時,可以繼續處理查詢請求。
-
復制在 slave 側大部分也是非阻塞的。當然這個是可配的,如果在 「redis.conf」配置是「非阻塞」的,可以使用舊數據集處理查詢請求;如果配置的是「阻塞」的,slave 會返回一個 error 給客戶端。
怎么實現主從復制?
假設現在有兩個 Redis 服務器,地址分別為 「127.0.0.1:6379」 和 「127.0.0.1:12345」,如果在服務器 「127.0.0.1:12345」 執行以下命令:
127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK
那么服務器「127.0.0.1:12345」就是「127.0.0.1:6379」 的從服務器。「主從服務器的數據會保持一致」比如主服務器存儲數據:
127.0.0.1:6379> set msg "hello world"
OK
然后從服務器就能直接獲取數據:
127.0.0.1:12345>get msg
"hello world"
刪除數據也是一樣,主從會保持一致。
主從復制原理
首先,Redis 的復制分為「同步」(sync)和「命令傳播」(command propagate)兩個操作:
-
同步操作用於將從服務器數據庫的狀態更新為主服務器所處的狀態。
-
命令傳播則相反,它主要作用在主服務器的數據庫狀態更改時,導致主從服務器的數據庫狀態出現不一致時,讓主從回到一致的的過程。
接下來詳細說說這兩種復制。
同步

文字解說:
-
客戶端向從服務器發送 「SLAVEOF」 命令,先是判斷是否是第一次復制,第一次是復制一般是剛開始組建主從關系。
-
是第一次復制:從服務器會向主服務器發送 「PSYNC ? -1」 命令,請求主服務器執行「完整重同步」操作。
-
主服務器接到「完整重同步」請求之后,將在后台執行 「BGSAVE」 命令,在后台生成一個 「RDB」 文件,並使用一個「復制積壓緩沖區」記錄從現在開始執行的所有寫命令。
-
BGSAVE 命令執行完畢之后,主服務器會將 RDB 文件以及 緩沖區中記錄的寫命令發送給從服務器,還會向從服務器返回 「+FULLRESYNC [主服務器 ID] [復制偏移量]」(和圖中的 偏移量 是一個)。
-
從服務器接收到后,會載入 RDB 文件,並執行 主服務器給的 寫命令,以此來達到和主服務器一致的數據狀態。
-
如果不是第一次復制,那么說明從服務器可能是斷線,導致和主服務器數據狀態不一致,需要同步主服務器的數據。那么從服務器會按照下面的步驟來請求部分同步。
-
向主服務器發送 「PSYNC [主服務器 ID] [復制偏移量]」(這個是第一次復制時主服務器傳過來的),「主服務器 ID」 時斷線前的主服務器,用於定位去同步那個主服務器的;「復制偏移量」是上一次同步的位置,用於定位具體的同步位置的。
-
主服務器接收到從服務器的命令后,並找到相應同步的位置后,會給從服務器發送 「+CONTINUE」 命令,表示將於從服務器執行部分同步操作,之后主服務器會將保存在「復制積壓緩沖區」對應 「復制偏移量」之后的所有數據發送給從服務器,但是如果找不到偏移量之后的數據,就會進行「完整同步」,這樣就可以讓從服務器達到和主服務器一致的狀態。
命令傳播
主從服務器同步成功后,並不會一致保持這個狀態,主服務器可能會執行寫命令,這也主從數據就不知一致了。為了處理這種問題,「主服務器會把自己執行的寫命令發送給從服務器,當從服務器執行完這些命令之后,主從服務器的數據就一致了」。

在命令傳播階段,從服務器默認會以每秒一次的頻率,向主服務器發送命令:「REPLCONF ACK <replication_offset>」<replication_offset> 是從服務器當前的復制偏移量。發送 REPLCONF ACK 命令對於主從服務器有三個作用:
-
檢測主從服務器的網絡狀態。
-
輔助實現 min-slaves 選項。
-
檢測命令丟失。
關鍵詞講解
-
「主服務器 ID」:用於標識一個服務器。
-
每個服務器,無論是主服務器還是從服務器都有屬於自己獨一無二的 服務器 ID。
-
ID 在服務器啟動時生成,由 40 個隨機的十六進制字符組成。
-
「復制積壓緩沖區」:復制積壓緩沖區是由主服務器維護的一個固定長度、先進先出(FIFO)隊列,默認大小為 1MB。
總結
Redis 主從復制主要是通過 PSYNC 命令實現。復制分為 「部分復制」 以及 「完整復制」。部分復制通過 復制偏移量、復制積壓緩沖區、服務器 ID 來實現。完整復制通過 RDB 以及 復制積壓緩沖區來實現。主從復制主要解決的是 數據備份、讀寫分離的問題。
參考文章:
https://xie.infoq.cn/article/b89461f4b68913a8c3a8627b8
