1. 程式人生 > 程式設計 >Redis 複製過程詳解

Redis 複製過程詳解

Redis 的複製功能分為同步( sync )和命令傳播( command propagate )兩個步驟:

  • 同步用於將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態。
  • 命令傳播則用於在主伺服器的資料庫狀態被修改,導致主從伺服器的資料庫狀態出現不一致時,讓主從伺服器的資料庫重新回到一致狀態。

同步

Redis 使用 psync 命令完成主從資料同步,同步過程分為:全量複製和部分複製。

全量複製:一般用於初次複製場景,它會把主節點全部資料一次性傳送給從節點傳送給從節點,當資料量較大時,會對主從節點和網路造成很大的開銷。

部分複製:用於處理在主從複製中因網路閃斷等原因造成的網路丟失場景,當從節點再次連線上主節點後,如果條件允許,主節點會補發丟失資料給從節點。因為補發的資料遠遠小於全量資料,可以有效避免全量複製的過高開銷。

psync 命令執行需要以下元件支援:

  • 主從節點各自複製偏移量
  • 主節點複製積壓緩衝區
  • 主節點執行 id

參與複製的從節點都會維護自身複製偏移量。主節點在處理完寫命令後,會把命令的位元組長度做累加記錄,統計在 info replication 中的 master_repl_offset 指標中。 從節點在接收到主節點傳送的命令後,也會累加記錄自身的偏移量,並且會每秒鐘上報自身的複製偏移量給主節點。 通過對比主從節點的複製偏移量,可以判斷主從節點資料是否一致。

複製積壓緩衝區是儲存在主節點的一個固定長度的佇列,預設大小為 1MB,當主節點有連線的從節點時被建立。主節點響應寫命令時,不但會把命令傳送給從節點,還會寫入複製積壓緩衝區中。

複製積壓緩衝區大小有限,只能儲存最近的複製資料,用於部分複製和複製命令丟失時的資料補救。

每個 Redis 節點啟動後都會動態分配一個 40 位的十六進位制字串作為執行 ID。執行 ID 的主要作用是用來唯一標識 Redis 節點,比如說從節點儲存主節點的執行 ID 來識別自己正在複製的時哪個主節點。

全量同步

slaveof 命令的執行

    1. 從節點傳送 psync 命令進行資料同步,由於是第一次進行復制,從節點沒有複製偏移量和主節點的執行ID,所以傳送的命令時 PSYNC ? -1。
    1. 主節點根據 PSYNC ? -1 解析出當前為全量複製,回覆 + FULLRESYNC 響應。
    1. 從節點接收主節點的響應資料儲存執行 ID 和偏移量 offset。
    1. 主節點執行 bgsave 儲存 RDB 檔案到本地,有關 RDB 的知識可以檢視《Redis RDB 持久化詳解》
    1. 主節點傳送 RDB 檔案給從節點,從節點把接收的 RDB 檔案儲存在本地並直接作為從節點的資料檔案,接收完 RDB 後從節點列印相關日誌,可以在日誌中檢視主節點傳送的資料量。

需要注意,對於資料量較大的主節點,比如生成的 RDB 檔案超過 6GB 以上時要格外小心。如果傳輸 RDB 的時間超過 repl-timeout 所配置的值,從節點將發起接收 RDB 檔案並清理已經下載的臨時檔案,導致全量複製失敗。

    1. 對於主節點開始儲存 RDB 快照到從節點接收完成期間,主節點仍然響應讀命令,因此主節點會把這期間寫命令儲存在複製客戶端緩衝區內,當從節點載入完 RDB 檔案後,主節點再把緩衝區內的資料傳送給從節點,保證主從之間資料一致性。

如果主節點建立和傳輸 RDB 的時間過長,可能會出現主節點複製客戶端緩衝區溢位。預設配置為 client-output-buffer-limit slave 256MB 64MB 60,如果60s內緩衝區消耗持續大於64MB或者直接超過256MB時,主節點將直接關閉複製客戶端連線,造成全量同步失敗。

    1. 從節點接收完主節點傳送來的全部資料後會清空自身舊資料,該步驟對應如下日誌。
    1. 從節點清空資料後開始載入 RDB 檔案,對於加大的 RDB 檔案,這一步操作依然比較耗時,可以通過計算日誌之間的時間差來判斷載入 RDB 的總耗時。
    1. 收到 SYNC 命令的主伺服器執行 BGSAVE 命令,在後臺生成一個 RDB 檔案,並使用一個緩衝區記錄從現在開始執行的所有寫命令。
    1. 當主伺服器的 BGSAVE 命令執行完畢時,主伺服器會將 GBSAVE 命令生成的 RDB 檔案傳送給從伺服器,從伺服器接收並載入這個 RDB 檔案,將自己的資料庫狀態更新至主伺服器執行 BGSAVE 命令時的資料庫狀態。
    1. 主伺服器將記錄在緩衝區裡邊的所有寫命令傳送給從伺服器,從伺服器執行這些寫命令,將自己的資料庫狀態更新至主伺服器資料庫當前所處的狀態。

通過分析全量複製的所有流程,讀者會發現全量複製是一個非常耗時費力的操作。它時間開銷主要包括:

  • 主節點 bgsave 時間
  • RDB 檔案網路傳輸時間
  • 從節點清空資料時間
  • 從節點載入 RDB 的時間
  • 可能的 AOF 重寫時間

全量同步過程中不僅會消耗大量時間,還會進行多次持久化相關操作和網路資料傳輸,這期間會大量消耗主從節點所在伺服器的 CPU、記憶體和網路資源。所以,除了第一次複製是採用全量同步無法避免,其他場景應該規避全量複製,採取部分同步功能。

部分同步

部分複製主要是 Redis 針對全量複製的過高開銷做出的一種優化措施,使用 psync {runId} {offset} 命令實現。當從節點正在複製主節點時,如果出現網路閃斷或者命令丟失等異常情況時,從節點會向主節點要求補發丟失的命令資料,如果主節點的複製積壓緩衝區存在這部分資料則直接傳送給從節點,這樣就保證了主從節點複製的一致性。補發的這部分資料一般遠遠小於全量資料,所以開銷很小。

    1. 當主從節點之間網路出現中斷時,如果超過了 repl-timeout 時間,主節點會認為從節點故障並中斷複製連線。
    1. 主從連線中斷期間主節點依然響應命令,但因複製連線中斷命令無法傳送給從節點,不過主節點內部存在複製積壓緩衝區( repl-backlog-buffer ),依然可以儲存最近一段時間的寫命令資料,預設最大快取 1MB。
    1. 當主從節點網路恢復後,從節點會再次連上主節點。
    1. 當主從連線恢復後,由於從節點之前儲存了自身已複製的偏移量和主節點的執行ID。因此會把它們作為 psync 引數傳送給主節點,要求進行補發複製操作。
    1. 主節點接到 psync 命令後首先核對引數 runId 是否與自身一致,如果一致,說明之前複製的是當前主節點;之後根據引數 offset 在自身複製積壓緩衝區查詢,如果偏移量之後的資料存在緩衝區中,則對從節點傳送 +CONTINUE 響應,表示可以進行部分複製。
    1. 主節點根據偏移量把複製積壓緩衝區裡的資料傳送給從節點,保證主從複製進入正常狀態。

心跳檢測

主從節點在建立複製後,它們之間維護著長連線並彼此傳送心跳命令,如下圖所示。

主從心跳判斷機制如下所示:

    1. 主從節點彼此都有心跳檢測機制,各自模擬成對方的客戶端進行通訊,通過 client list 命令檢視複製相關客戶端資訊,主節點的連線狀態為 flags=M,從節點連線狀態為 flags=S。
    1. 主節點預設每隔 10 秒對從節點傳送 ping 命令,判斷從節點的存活性和連線狀態。可以通過引數 repl-ping-slave-period 控制傳送頻率。
    1. 從節點在主執行緒中每隔 1 秒傳送 replconf ack { offset } 命令,給主節點上報自己當前的複製偏移量。

replconf 命令不僅能實時監測主從節點網路狀態,還能上報從節點複製偏移量。主節點會根據從節點上傳的偏移量檢查複製資料是否丟失,如果從節點資料丟失,再從主節點的複製快取區中拉取丟失的資料傳送給該從節點。

非同步複製和命令傳播

主節點不但負責資料讀寫,還負責把寫命令同步給從節點。寫命令的傳送過程是非同步完成,也就是說主節點自身處理完寫命令後直接返回給客戶端,並不等待從節點複製完成。

這個非同步過程由命令傳播來處理,它不僅會將寫命令傳送給所有從伺服器,還會將寫命令入隊到複製積壓緩衝區裡邊。

後記

個人部落格,歡迎來玩