主從複製 這是《Redis設計與實現》系列的文章,系列導航:Redis設計與實現筆記 SLAVEOF 新舊複製功能 舊版複製功能 舊版複製功能的實現為 同步 和 命令傳播: 當剛連上Master時,要做一次全同步: sequenceDiagram participant Slave particip ...
主從複製
這是《Redis設計與實現》系列的文章,系列導航:Redis設計與實現筆記
SLAVEOF
新舊複製功能
舊版複製功能
舊版複製功能的實現為 同步 和 命令傳播:
當剛連上Master時,要做一次全同步:
sequenceDiagram participant Slave participant Master Slave->>Master: SYNC Master->>Master: BGSAVE Master->>Master: 記錄此時的命令到緩衝區中 Master->>Slave: 發送RDB Master->>Slave: 發送命令緩衝區中的命令之所以要用到緩衝區是因為,在主節點進行 BGSAVE 的過程中如果有命令執行,那麼我們要把這些命令也記錄下來。
之後,主從節點之間只用 命令傳播 就可以做到同步了,也就是說主節點執行什麼命令,從節點跟著執行。(當然,一些隨機、時間類的函數會直接轉換成定值)
舊版複製的缺陷
如果從節點斷線後重新連接,舊版複製功能的效率很低,因為為了讓從伺服器補足一小部分的確實卻要進行一次 SYNC 命令。
為什麼低效:
- 主節點 BGSAVE 要消耗大量的CPU、記憶體、IO資源
- 主節點發送需要消耗網路資源
- 從節點需要載入,且載入期間處於阻塞狀態
新版複製功能
用 PSYNC
命令代替 SYNC。
PSYNC 具有 完整重同步 和 部分重同步 兩種模式,分別針對初次同步和重新同步兩種場景。
複製功能的實現
複製的實現
複製的一些具體的細節,當進行複製時:
-
從伺服器設置主伺服器的地址和埠
struct redisServer{ //... char *masterhost; int masterport; //... }
-
建立套接字連接,並關聯一個專門處理複製工作的文件事件處理器
-
發送 PING 命令,檢查套接字和主伺服器的狀態是否正常
-
身份驗證,主從必須配置一致且密碼正確(如果有)才能通過驗證
-
發送埠信息:主節點也得知道給從節點的哪個埠發消息,不是麽
-
同步:乾正事兒嘍
這裡書上說:
- 在同步操作執行之前,只有從伺服器是主伺服器的客戶端,但是在執行同步操作之後,主伺服器也會成為從伺服器的客戶端。
- 正是因為主服務成為了從伺服器的客戶端,所以主伺服器才能通過發送寫命令來改變從伺服器的資料庫狀態。
我想了想,似乎一般確實都是客戶端改變服務端的數據的,所以這麼說倒也在理,但是服務端不是也可以給客戶端發送數據麽?所以這裡可能和 Redis 的具體實現有關?
-
命令傳播:進入了第二個階段
如何部分重同步
要關註的三個部分:
-
複製偏移量:主從伺服器都有複製偏移量,通過這個值判斷主從是否處於一致狀態
-
主伺服器的複製積壓緩衝區:保存執行命令的歷史記錄
一個固定長度(預設1MB)的 FIFO 的隊列,當主從不一致時可以計算並從中獲取缺少的命令。
由於固定長度,所以如果缺的多了就只能進行完整重同步了。
大小一般設為 斷連平均時間 * 每秒的命令數,安全起見再乘以2。
-
伺服器的運行 ID
畢竟只有 ID 一致同步才有意義,否則說明換主人了,那還是全同步吧
PSYNC的邏輯
graph LR; S(接收到SLAVEOF命令) --> A{第一次複製?} A --Y--> A1[發送PSYNC ? -1] --> E1(返回+FULLRESYNC <runid> <offset>) A --N--> A2[發送PSYNC <runid> <offset>] --> B{主伺服器返回 +CONTINUE} B --N--> E1 B --Y--> E2[執行部分重同步]主要是判斷 是否是第一次複製 、 是否是同一個主伺服器,從而決定是部分重同步還是全同步。
上圖沒有展示的是,如果主伺服器不支持 PSYNC,則返回 -ERR
心跳檢測
心跳檢測:在命令傳播階段,從伺服器預設每秒發送一次心跳:REPLCONF ACK <replication_offset>
。
作用有三:
-
檢測主從伺服器的網路狀態
-
輔助實現 min-slaves 配置選項
min-slaves-to-write、min-slaves-max-lag 可以防止發生腦裂現象
-
通過 offset 檢測命令是否丟失