新手村:Redis進階篇三——主從複製

Redis進階篇三——主從複製

1. 主從複製概述

前面幾篇內容咱們都是在一臺 Redis 服務器上進行操做,包括數據的讀、寫以及備份操做。本篇要介紹的主從複製,是指將一臺 Redis 服務器的數據,複製到其餘 Redis 服務器,咱們將前者稱爲主節點 master,將後者稱爲從節點 slave。在這個過程當中,數據的複製是單向的,即只能從主節點到從節點。 在默認狀況下,咱們開啓的每臺 Redis 服務器都是獨立的主節點,在主從複製中,一個主節點能夠有多個從節點,但一個從節點只能有一個主節點。以下圖所示:web

graph TB;
        A(master)-->B(slave1)
        A-->C(slave2)
        A-->D(......)
複製代碼

主從複製的做用主要包括:redis

  1. 數據冗餘:主從複製實現了數據的熱備份,是持久化以外的一種數據冗餘方式。
  2. 故障恢復:當主節點出現問題時,能夠由從節點提供服務,實現快速的故障恢復;其實是一種服務的冗餘。
  3. 負載均衡:在主從複製的基礎上,配合讀寫分離,寫數據時應用鏈接主節點,讀數據時應用鏈接從節點,分擔服務器負載;尤爲是在寫少讀多的場景下,經過多個從節點分擔讀負載,能夠大大提升 Redis 服務器的併發量。
  4. 高可用基礎:主從複製是哨兵模式和集羣可以實施的基礎。

2. 實現原理

主從複製的實現過程大體能夠分爲3個階段:創建鏈接、數據同步、命令傳播數據庫

2.1 創建鏈接階段

在實現主從複製的第一步固然是主從節點之間創建鏈接,這一階段也能夠稱爲準備階段,主要包含了如下幾個步驟:windows

  1. 保存主節點信息緩存

    從節點服務器內部包含了 masterhost 和 masterport 字段,分別存儲了主節點的 IP 和 port 信息。slaveof 是異步命令,從節點完成主節點 IP 和 port 的保存後,向發送 slaveof 命令的客戶端直接返回 OK,實際的複製操做在這以後纔開始進行。  安全

  2. 創建 socket 鏈接服務器

    從節點每秒調用 1 次複製定時函數 replicationCron(),若是發現了有主節點能夠鏈接,便會根據主節點的 IP 和 port,建立 socket 鏈接。鏈接成功後從節點會爲該 socket 創建一個專門處理複製工做的文件事件處理器,負責後續的複製工做,如接收 RDB 文件、接收命令傳播等;主節點在接收到從節點的 socket 鏈接後(即 accept 以後),爲該 socket 建立相應的客戶端狀態,並將從節點看作是鏈接到主節點的一個客戶端,後面的步驟會以從節點向主節點發送命令請求的形式來進行。  網絡

  3. 發送 ping 命令併發

    從節點成爲主節點的客戶端以後,發送 ping 命令進行首次請求,目的是檢查 socket 鏈接是否可用,以及主節點當前是否可以處理請求。從節點發送 ping 命令後,可能出現 3 種狀況:負載均衡

    1. 返回 pong:說明 socket 鏈接正常,且主節點當前能夠處理請求,複製過程繼續。
    2. 超時:必定時間後從節點仍未收到主節點的回覆,說明 socket 鏈接不可用,則從節點斷開 socket 鏈接,並重連。
    3. 返回 pong 之外的結果:若是主節點返回其餘結果,如正在處理超時運行的腳本,說明主節點當前沒法處理命令,則從節點斷開 socket 鏈接,並重連。  
  4. 身份驗證

    若是從節點中配置文件中設置了 masterauth 選項,則從節點須要向主節點進行身份驗證;沒有設置該選項,則不須要驗證。從節點進行身份驗證是經過向主節點發送 auth 命令進行的,auth 命令的參數即爲配置文件中的 masterauth 的值。若是主節點設置密碼的狀態與從節點 masterauth 的狀態一致(一致是指都存在,且密碼相同,或者都不存在),則身份驗證經過,複製過程繼續;若是不一致,則從節點斷開 socket 鏈接,並重連。  

  5. 發送從節點端口信息

    身份驗證以後,從節點會向主節點發送其監聽的端口號,主節點將該信息保存到該從節點對應的客戶端的 slave_listening_port 字段中。該端口信息除了在主節點中執行 info replication 命令時顯示之外,沒有其餘做用。

2.2 數據同步階段

在主從節點之間創建鏈接以後,就能夠開始進行數據的同步,這一階段也能夠理解是從節點中數據的初始化。 Redis 的數據同步有兩個重要的命令:syncpsync,sync 命令是 Redis2.8 之前請求同步數據的命令,同步方式是全量複製;psync 命令是 Redis2.8 算是對 sync 優化後的命令,同步方式能夠是全量複製或增量複製。 這裏提到兩個數據同步的關鍵:全量複製增量複製,先作個簡單介紹:

  • 全量複製:用於初次複製或其餘沒法進行增量複製的狀況,將主節點中的全部數據都發送給從節點。
  • 增量複製:用戶網絡中斷等狀況的複製,只將中斷期間主節點執行的寫命令發送給從節點,相比於全量複製更加高效。

若是網絡中斷的時間過長,會致使主節點沒有可以完整地保存中斷期間執行的寫命令,則沒法進行增量複製,仍使用全量複製。

2.2.1 psync命令

在講解 psync 命令以前,咱們先講幾個須要知道的概念:

  1. 複製偏移量 ( offset )

    主節點和從節點會各自維護一個複製偏移量 ( offset ),表明的是主節點向從節點傳遞的字節數。主節點每次向從節點傳播 N 個字節數據時,主節點的 offset 增長 N;同理,當從節點每次收到主節點傳來的 N 個字節數據時,從節點的 offset 增長 N。offset 的值咱們能夠經過 info replication 命令返回的信息中看到。經過比較主節點和從節點的 offset 能夠判斷數據庫狀態是否一致,若是二者的 offset 相同,則一致,不然不一致;也能夠根據二者的 offset 找出從節點缺乏的數據。  

  2. 複製積壓緩存區

    複製積壓緩存區是由主節點維護的一個固定長度的先進先出 ( FIFO) 隊列,默認大小是 1MB。當主節點開始有從節點時,主節點建立複製積壓緩存區,其做用是備份主節點最近發送給從節點的數據。 在命令傳播階段,主節點除了將寫命令發送給從節點,還會發送一份給複製積壓緩衝區,做爲寫命令的備份;除了存儲寫命令,複製積壓緩衝區中還存儲了其中的每一個字節對應的複製偏移量 ( offset )。因爲複製積壓緩衝區定長且是先進先出,因此它保存的是主節點最近執行的寫命令;時間較早的寫命令會被擠出緩衝區。 因爲該緩衝區長度固定且有限,所以能夠備份的寫命令也有限,當主從節點 offset 的差距過大超過緩衝區長度時,將沒法執行增量複製,只能執行全量複製。所以從節點將 offset 發送給主節點後,主節點根據 offset 和緩衝區大小決定可否執行增量複製:

    • 若是 offset 偏移量以後的數據仍然在複製積壓緩衝區裏,則執行增量複製
    • 若是 offset 偏移量以後的數據已不在複製積壓緩衝區中(數據已被擠出),則執行全量複製

     

  3. 服務器運行ID ( runid )

    不管是主節點仍是從節點,在啓動時都會自動生成一個隨機 ID ( runid ),由 40 個隨機的十六進制字符組成且每次啓動都不同。若是使用 IP 和 port 的方式識別一個節點,那麼當主節點重啓修改了 RDB/AOF 數據,從節點再基於偏移量進行復制將是不安全的,所以 runid 的主要做用是惟一識別一個 Redis 節點。經過 info server 命令能夠查看節點的 runid。 主從節點初次複製時,主節點將本身的 runid 發送給從節點,從節點將這個 runid 保存起來;當斷線重連時,從節點會將這個 runid 發送給主節點;主節點根據發送來的 runid 和當前本身的 runid 是否相同判斷可否進行增量複製:

    • 若是相同,說明主從節點以前同步過,主節點會繼續嘗試使用增量複製 ( 到底能不能增量複製還要看 offset 和複製積壓緩衝區的狀況 )
    • 若是不一樣,說明從節點在斷線前同步的 Redis 節點並非當前的主節點,只能進行全量複製

      psync 命令的格式:psync < runid > < offset > psync 的執行流程

psync執行流程
psync執行流程

首先從節點根據是否執行過 slaveof 命令決定如何調用 psync 命令:

  • 以前從未執行過 slaveof 或最近執行了 slaveof no one,則從節點發送 psync ? -1 命令,向主節點請求全量複製
  • 以前執行了 slaveof,則發送 psync < runid > < offset > 命令,其中 runid 爲上次複製的主節點的 runid,offset 爲上次複製截止時從節點保存的複製偏移量

主節點根據收到的 psync 命令以及當前服務器的狀態,決定執行全量複製仍是增量複製:

  • 若是主節點版本低於 Redis2.8,則返回 -ERR 回覆,此時從節點從新發送 sync 命令執行全量複製
  • 若是主節點版本夠新,且 runid 與從節點發送的 runid 相同,且從節點發送的 offset 以後的數據在複製積壓緩衝區中都存在,則回覆 +CONTINUE,表示將執行增量複製,從節點等待主節點發送其缺乏的數據便可
  • 若是主節點版本夠新,可是 runid 與從節點發送的 runid 不一樣,或從節點發送的 offset 以後的數據已不在複製積壓緩衝區中(在隊列中被擠出了),則回覆 +FULLRESYNC < runid > < offset >,表示要進行全量複製,其中 runid 表示主節點當前的 runid,offset 表示主節點當前的 offset,從節點保存這兩個值,以備使用

2.2.2 全量複製

全量複製是主從節點第一次創建主從複製關係時必須經歷的階段,複製流程以下: 全量複製

  1. 從節點判斷須要進行全量複製,向主節點傳遞命令 psync ? -1 (因爲不知道主節點的 runid 和 offset,因此傳 -1)
  2. 主節點收到全量複製的請求,傳遞 FULLRESYNC < runid > < offset > 返回主節點的 runid 和 offset
  3. 從節點保存主節點的 runid 和 offset 信息
  4. 主節點執行 bgsave,在後臺生成 RDB 文件,並使用一個緩衝區(稱爲複製緩衝區)記錄從如今開始執行的全部寫命令
  5. 主節點的 bgsave 執行完成後,將 RDB 文件發送給從節點
  6. 主節點執行 send buffer 操做,向從節點同步生成快照過程當中的緩存命令
  7. 從節點清空舊數據並加載 RDB 文件
  8. 若是從節點開啓了 AOF,則會觸發 bgrewriteaof 的執行,從而保證AOF文件更新至主節點的最新狀態

經過全量複製的過程能夠看出,整個過程是十分消耗資源和時間的:

  • 主節點經過 bgsave 命令執行 fork 操做建立子進程進行 RDB 持久化,該過程是很是消耗 CPU、內存和磁盤 I/O 資源
  • 主節點經過網絡向從節點傳遞 RDB 文件,耗費主服務器大量的網絡資源包括帶寬和流量,並對主服務器響應命令請求的時間產生影響
  • 從節點清空舊數據並加載新的 RDB 文件的過程是阻塞的而沒法響應其餘命令請求,若是執行 bgrewriteaof 操做也會帶來額外的消耗

2.2.3 增量複製

當從節點正在複製主節點時出現網絡異常或其餘異常,從節點會請求主節點補發缺失的命令數據,主節點只須要將複製積壓緩衝區的數據發送到從節點便可。相比於全量複製,增量複製的成本代價小不少,其流程以下: 增量複製

  1. 網絡發生抖動,主節點和從節點斷開鏈接
  2. 主節點會將寫命令備份到複製積壓緩衝區中
  3. 從節點再次鏈接上主節點
  4. 從節點經過命令 psync < runid > < offset > 向主節點告知以前鏈接的 runid 和本身的偏移量
  5. 主節點進行校驗後向從節點返回命令 CONTINUE 表示能夠進行增量複製
  6. 主節點將緩衝區的數據發送到從節點

2.3 命令傳播階段

數據同步階段完成後,主從節點進入命令傳播階段;在這個階段主節點將本身執行的寫命令發送給從節點,從節點接收命令並執行,從而保證主從節點數據的一致性。 在命令傳播階段,除了發送寫命令,主從節點還維持着心跳機制:PING 和 REPLCONF ACK。心跳機制對於主從複製的超時判斷、數據安全等有做用心跳機制 每隔指定的時間,主節點會向從節點發送 PING 命令,做用主要是讓從節點進行超時判斷。PING 命令的發送頻率由 repl-ping-slave-period 參數控制,單位是秒,默認值是 10s。 從節點會向主節點發送 REPLCONF ACK 命令,頻率是每秒 1 次,命令的格式爲:REPLCONF ACK < offset >,其中 offset 是從節點中保存的複製偏移量。 REPLCONF ACK命令的做用包括:

  • 實時監測主從節點網絡狀態:該命令會被主節點用於複製超時的判斷。此外,在主節點中使用 info replication 命令能夠看到其從節點的狀態中的 lag 值,表明的是主節點上次收到該 REPLCONF ACK 命令的時間間隔,在正常狀況下,該值應該是 0 或 1。

  • 檢測命令丟失:從節點發送了自身保存的 offset,主節點會與本身的 offset 進行對比,若是從節點數據缺失(如網絡丟包),主節點會推送缺失的數據(這裏也會利用複製積壓緩衝區)。  

    offset 和複製積壓緩衝區,不只能夠用於增量複製,也能夠用於處理命令丟失等情形;區別在於前者是在斷線重連後進行的,然後者是在主從節點沒有斷線的狀況下進行的。

  • 輔助保證從節點的數量和延遲:主節點中使用 min-slaves-to-write 和 min-slaves-max-lag 參數,來保證主節點在不安全的狀況下不會執行寫命令;所謂不安全,是指從節點數量太少或延遲太高。  

    例如 min-slaves-to-write 和 min-slaves-max-lag 分別是 3 和 10,含義是若是從節點數量小於 3 個,或全部從節點的延遲值都大於 10s,則主節點拒絕執行寫命令。而這裏從節點延遲值的獲取,就是經過主節點接收到 REPLCONF ACK 命令的時間來判斷的,即前面所說的 info Replication中的 lag 值。

3. 具體實現

下面簡單演示一下如何實現 Redis 主從複製,這裏我是在一臺機器上模擬多個 Redis 服務器,與實際生產環境相比,基本配置大體相同,關鍵都在於 IP 地址和端口號變化。

3.1 配置主從關係

首先將 redis 文件夾複製兩份做爲兩個從節點,複製在同一文件下便可,命名自定,這裏我分別重命名爲 redis_slave1 和 redis_slave2。

複製文件
複製文件

主從複製的開啓是從從節點發起的,不須要咱們對主節點作任何事情,所以咱們對 slave 文件的配置文件進行修改,這裏咱們須要修改的配置文件有兩個:redis.windows.confredis.windows-service.conf

配置的文件
配置的文件

從節點開啓主從配置,有 3 種方式:

  • 配置文件:在配置文件加入 slave < masterip > < masterport >
  • 啓動命令:redis-server啓動命令後加入 --slave < masterip > < masterport >
  • 客戶端命令:Redis 服務器啓動後,直接經過客戶端執行命令:slave < masterip > < masterport >

這裏咱們修改 redis_slave1 的配置文件來開啓主從複製,對於 redis_slave2,則用客戶端命令的方式,啓動命令方式和客戶端命令方式差很少,感興趣的能夠本身試一試。下面以 redis_slave1 的配置文件爲例:

配置端口
配置端口
配置文件方式
配置文件方式

從上面兩張圖中,咱們將 slave1 的端口號改成 6380,並從屬於 6379端口即主節點。對於 slave2,咱們僅僅將端口號改成 6381。

無論修改 slave1 仍是 slave2 的配置文件,redis.windows.conf 和 redis.windows-service.conf 這兩個配置文件都要進行修改。

3.2 創建主從關係

修改好配置文件後,咱們分別打開三個 Redis 服務端和客戶端。咱們先打開主節點的服務端再打開 slave1 和 slave2 的服務端。當咱們打開 salve1 時,能夠觀察到服務端發生變化。

openslave1 openmaster

從以上兩圖中的紅框部分,能夠發現打開 slave1 以後就開啓了主從複製,而且 slave1 對主節點進行了同步數據。接下來咱們看看打開 slave2 以後有沒有什麼變化。

openslave2
openslave2

僅僅是正常打開服務端,並無對主節點形成什麼影響。 除了看服務端的變化,咱們也可使用 info replication 命令在客戶端查看不一樣之處。

master_info1 slave1_info1 slave2_info1

從以上三張圖中,咱們也能夠看出端口 6379 和 6380 創建了主從複製關係,且顯示了一些信息。而端口 6381 的角色爲 master,是一個獨立的主節點。 接着咱們使用客戶端命令的方式讓端口 6381 從屬於端口 6379,並觀察兩個端口的服務端發生的變化。

slave2_info2 slave2_connect master_change1 master_info2

當咱們在 slave2 的客戶端使用 slaveof 127.0.0.1 6379 命令創建主從關係後能夠在主從服務器端看到成功開啓主從複製的信息。在主從客戶端查看節點角色也能夠發現,slave2 的角色變成了 slave,主節點的從節點數量變成 2,且多了 slave2 的簡要信息。至此,咱們就創建了一個一主二從的主從複製關係。

3.3 測試主從關係

已經創建了一主二從的主從複製關係,接下來咱們作一些簡單的測試,以便對主從複製有更好的理解。

  1. 增量複製

    當主節點進行寫操做後,從節點可否獲取剛剛寫入的數據嗎? 增量複製 由上圖可見,從節點是能夠獲取到的。  

  2. 全量複製

    當從節點暫時斷開主從關係,從新鏈接後是否可以獲取斷開期間主節點寫入的數據? 咱們先在主節點中清空數據,再使用 slaveof no one 命令斷開 master 和 slave2 之間的主從關係,隨後 master 寫入 k1,k2 數據,當 slave2 從新與 master 創建主從關係後查看是否有剛剛寫入的數據。 全量複製 從上圖中,咱們能夠看到從新創建主從關係以後,從節點也會將數據複製過來。  

  3. 主從讀寫分離

    主節點能夠執行寫操做,從節點能夠執行寫操做嗎,主節點也能夠進行讀操做嗎? 讀寫分離 主節點能夠執行讀寫操做,但當咱們想在 salve1 中執行 set 命令時卻出現了報錯,說明從節點只能進行讀操做,主從節點之間作到了讀寫分離。 從節點是否可以實現寫操做呢?事實上是能夠的,在配置文件中經過 slave-read-only 控制了從節點的寫權限,進行更改後就能夠進行寫操做,可是從節點添加的數據主節點是獲取不到的。不過並不建議更改,不然就偏離了主從複製的初衷(PS:手癢的能夠試試)。 readonly  

  4. 主節點宕機

    當主節點退出服務端和客戶端後,兩個從節點的角色會發生變化嗎? masterdown 當咱們用 shutdown 命令讓主節點宕機後,再用 info replication 命令查看 slave1 和 slave2,能夠看到兩個從節點的角色都還是 slave。  

  5. 主節點宕機後恢復

    當主節點從宕機恢復後,主節點的角色是否還是 master? master_reopen master_info3 從以上兩圖能夠看出當咱們從新打開主節點的服務端和客戶端,兩個從節點馬上發起了主從複製請求併成功創建主從關係,主節點的角色也仍舊是 master。

使用配置文件方式配置的主從關係是持久性的,即當你配置的主從服務端開啓時會自動創建起主從關係,而啓動命令方式和客戶端命令方式短暫性創建主從複製。

4. 總結

本篇主要講述了 Redis 主從複製的實現原理和簡單的具體實現例子。主從複製在必定程度上解決了主節點的單點故障問題,當主節點故障時,能夠從從節點上保障數據的讀取以及找回數據的可能性;可是因爲全部的寫操做都是在主節點上進行,而後同步到從節點上,這必然會出現必定的延時問題,且當系統任務繁重或從節點數量過多時,問題會更顯著。

最後吟唱

本文使用 mdnice 排版

相關文章
相關標籤/搜索