Redis 複製

主從複製,是指將一臺Redis服務器的數據,複製到其他的Redis服務器。前者稱爲主節點(master),後者稱爲從節點(slave);數據的複製是單向的,只能由主節點到從節點。

默認情況下,每臺Redis服務器都是主節點;且一個主節點可以有多個從節點(或沒有從節點),但一個從節點只能有一個主節點。主從模型還支持樹狀模型,使得從節點不但可以複製主節點,同時可以爲作其他從節點的繼續向下層複製

一 基本概述

主從複製的作用

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

建立複製

需要注意,主從複製的開啓,完全是在從節點發起的;不需要我們在主節點做任何事情。

從節點開啓主從複製,有3種方式:

  1. 配置文件在從服務器的配置文件中加入:slaveof <masterip> <masterport>
  2. 啓動命令:redis-server啓動命令後加入 --slaveof <masterip> <masterport>
  3. 客戶端命令Redis服務器啓動後,直接通過客戶端執行命令:slaveof <masterip> <masterport>,則該Redis實例成爲從節點。

斷開復制

通過slaveof <masterip> <masterport>命令建立主從複製關係以後,可以通過slaveof no one斷開。需要注意的是,從節點斷開復制後,不會刪除已有的數據,只是不再接受主節點新的數據變化。


主從複製的實現原理

主從複製過程大體可以分爲3個階段:連接建立階段(即準備階段)、數據同步階段、命令傳播階段;過程如圖:

1 建立連接

  1. 保存主節點信息:執行slaveof(異步命令)後從節點只保存主節點的地址信息便直接返回,這時建立複製流程還沒有開始
  2. 建立socket鏈接:從節點(slave)內部通過每秒運行的定時任務維護複製相關邏輯,當定時任務發現存在新的主節點後,會嘗試與該節點建立網絡連接(如果從節點無法建立連接,定時任務會無限重試直到連接成功或者執行slaveof no one取消複製)
  3. 發送ping命令:從節點成爲主節點的客戶端之後,發送ping命令進行首次請求,目的是:檢查socket連接是否可用,以及主節點當前是否能夠處理請求。從節點發送ping命令後,可能出現3種情況
    1. 返回pong:說明socket連接正常,且主節點當前可以處理請求,複製過程繼續。
    2. 超時:一定時間後從節點仍未收到主節點的回覆,說明socket連接不可用,則從節點斷開socket連接,並重連。
    3. 返回pong以外的結果:如果主節點返回其他結果,如正在處理超時運行的腳本,說明主節點當前無法處理命令,則從節點斷開socket連接,並重連。
  4. 身份驗證:如果從節點中設置了masterauth選項,則從節點需要向主節點進行身份驗證;(發送auth命令)
  5. 同步數據集:主從複製連接正常通信後,對於首次建立複製的場景,主節點會把持有的數據全部發送給從節點,這部分操作是耗時最長的步驟。Redis在2.8版本以後採用新複製命令psync進行數據同步,原來的sync命令依然支持,保證新舊版本的兼容性。新版同步劃分兩種情況:全量同步和部分同步

  數據同步

Redis在2.8及以上版本使用psync命令(之前爲sync)完成主從數據同步,同步過程分爲:全量複製和部分複製。

  1. 全量複製:一般用於初次複製場景,Redis早期支持的複製功能只有全量複製,它會把主節點全部數據一次性發送給從節點,當數據量較大時,會對主從節點和網絡造成很大的開銷。
  2. 部分複製:用於處理在主從複製中因網絡閃斷等原因造成的數據丟失場景,當從節點再次連上主節點後,如果條件允許,主節點會補發丟失數據給從節點。因爲補發的數據遠遠小於全量數據,可以有效避免全量複製的過高開銷。

psync命令運行需要以下組件支持:

  1. 主從節點各自複製偏移量
  2. 主節點複製積壓緩衝區
  3. 主節點運行id

複製偏移量:

主節點和從節點分別維護一個複製偏移量(offset),代表的是主節點向從節點傳遞的字節數;主節點每次向從節點傳播N個字節數據時,主節點的offset增加N;從節點每次收到主節點傳來的N個字節數據時,從節點的offset增加N。

offset用於判斷主從節點的數據庫狀態是否一致:如果二者offset相同,則一致;如果offset不同,則不一致,此時可以根據兩個offset找出從節點缺少的那部分數據。例如,如果主節點的offset是1000,而從節點的offset是500,那麼部分複製就需要將offset爲501-1000的數據傳遞給從節點。而offset爲501-1000的數據存儲的位置,就是下面要介紹的複製積壓緩衝區。

複製積壓緩衝區

複製積壓緩衝區是保存在主節點上的一個固定長度的隊列(環形數組),默認大小爲1MB,當主節點有連接的從節點(slave)時被創建,這時主節點(master)響應寫命令時,不但會把命令發送給從節點,還會寫入複製積壓緩衝區。

由於該緩衝區長度固定且有限,因此可以備份的寫命令也有限,當主從節點offset的差距過大超過緩衝區長度時,將無法執行部分複製,只能執行全量複製。當從第一次進行全量時如果主這時候接受到大量的寫命令,會把複製積壓緩衝區的數據刷走(從還沒有複製的一部分),這時候就會複製失敗,從重新發起全量複製,爲了避免這種情況可以根據需要增大複製積壓緩衝區的大小(通過配置repl-backlog-size)

從節點將offset發送給主節點後,主節點根據offset和緩衝區大小決定能否執行部分複製:

  • 如果offset偏移量之後的數據,仍然都在複製積壓緩衝區裏,則執行部分複製;
  • 如果offset偏移量之後的數據已不在複製積壓緩衝區中(數據已被擠出),則執行全量複製。

服務器運行ID(runid)

每個Redis節點啓動後都會動態分配一個40位的十六進制字符串作爲運行ID。運行ID的主要作用是用來唯一識別Redis節點,比如從節點保存主節點的運行ID識別自己正在複製的是哪個主節點。如果只使用ip+port的方式識別主節點,那麼主節點重啓變更了整體數據集(如替換RDB/AOF文件),從節點再基於偏移量複製數據將是不安全的,因此當運行ID變化後從節點將

做全量複製。可以運行info server命令查看當前節點的運行ID。

注:需要注意的是Redis關閉再啓動後,運行ID會隨之改變

這時可以使用debug reload令重新加載RDB並保持運行ID不變,從而有效避免不必要的全量複製(debug reload命令會阻塞當前Redis節點主線程,阻塞期間會生成本地RDB快照並清空數據之後再加載RDB文件。因此對於大數據量的主節點和無法容忍阻塞的應用場景,謹慎使用。)

 

全量複製實現過程及原理,過程如下:

  1. 從節點判斷無法進行部分複製(第一次複製 或 從在主的複製積壓緩衝區中沒有要複製的數據),具體過程需要在講述了部分複製原理後再介紹。
  2. 主節點收到全量複製的命令後,執行bgsave,在後臺生成RDB文件,並使用一個緩衝區(稱爲複製緩衝區)記錄從現在開始執行的所有寫命令
  3. 主節點的bgsave執行完成後,將RDB文件發送給從節點;從節點首先清除自己的舊數據,然後載入接收的RDB文件,將數據庫狀態更新至主節點執行bgsave時的數據庫狀態
  4. 主節點將前述複製緩衝區中的所有寫命令發送給從節點,從節點執行這些寫命令,將數據庫狀態更新至主節點的最新狀態
  5. 如果從節點開啓了AOF,則會觸發bgrewriteaof的執行,從而保證AOF文件更新至主節點的最新狀態

通過全量複製的過程可以看出,全量複製是非常重型的操作:

  1. 主節點通過bgsave命令fork子進程進行RDB持久化,該過程是非常消耗CPU、內存(頁表複製)、硬盤IO的;
  2. 主節點通過網絡將RDB文件發送給從節點,對主從節點的帶寬都會帶來很大的消耗
  3. 從節點清空老數據、載入新RDB文件的過程是阻塞的,無法響應客戶端的命令;如果從節點執行bgrewriteaof,也會帶來額外的消耗

部分複製

在瞭解了複製偏移量、複製積壓緩衝區、節點運行id之後,本節將介紹psync命令的參數和返回值,從而說明psync命令執行過程中,主從節點是如何確定使用全量複製還是部分複製的。

psync命令的執行過程可以參見下圖:

(1)首先,從節點根據當前狀態,決定如何調用psync命令:

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

(2)主節點根據收到的psync命令,及當前服務器狀態,決定執行全量複製還是部分複製:

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

需要注意的是,複製緩衝區是客戶端輸出緩衝區的一種,主節點會爲每一個從節點分別分配複製緩衝區;而複製積壓緩衝區則是一個主節點只有一個,無論它有多少個從節點。


三 心跳

主從節點在建立複製後,它們之間維護着長連接並彼此發送心跳命令

主——》從:主節點默認每隔10秒對從節點發送ping命令,判斷從節點的存活性

和連接狀態。可通過參數repl-ping-slave-period控制發送頻率。

從——》主:從節點在主線程中每隔1秒發送replconf ack{offset}命令,給主節點

上報自身當前的複製偏移量,作用如下:

  1. 實時監測主從節點網絡狀態:該命令會被主節點用於複製超時的判斷。此外,在主節點中使用info Replication,可以看到其從節點的狀態中的lag值,代表的是主節點上次收到該REPLCONF ACK命令的時間間隔,在正常情況下,該值應該是0或1
  2. 檢測命令丟失:從節點發送了自身的offset,主節點會與自己的offset對比,如果從節點數據缺失(如網絡丟包),主節點會推送缺失的數據(這裏也會利用複製積壓緩衝區)。注意,offset和複製積壓緩衝區,不僅可以用於部分複製,也可以用於處理命令丟失等情形;區別在於前者是在斷線重連後進行的,而後者是在主從節點沒有斷線的情況下進行的。
  3. 輔助保證從節點的數量和延遲:Redis主節點中使用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值。

規避全量複製:

(1)數據同步階段:

在主從節點進行全量複製bgsave時,主節點需要首先fork子進程將當前數據保存到RDB文件中,然後再將RDB文件通過網絡傳輸到從節點。如果RDB文件過大,主節點在fork子進程+保存RDB文件時耗時過多,可能會導致從節點長時間收不到數據而觸發超時;此時從節點會重連主節點,然後再次全量複製,再次超時,再次重連……這是個悲傷的循環。爲了避免這種情況的發生,除了注意Redis單機數據量不要過大,另一方面就是適當增大repl-timeout值,具體的大小可以根據bgsave耗時來調整。

(2)複製積壓緩衝區不足:

當主從節點網絡中斷後,從節點再次連上主節點時會發送psync{offset}{runId}命令請求部分複製,如果請求的偏移量不在主節點的積壓緩衝區內,則無法提供給從節點數據,因此部分複製會退化爲全量複製。針對這種情況需要根據網絡中斷時長,寫命令數據量分析出合理的積壓緩衝區大小。網絡中斷一般有閃斷、機房割接、網絡分區等情況。這時網絡中斷的時長一般在分鐘級(net_break_time)。寫命令數據量可以統計高峯期主節點每秒info replication的master_repl_offset差值獲取(write_size_per_minute)。積壓緩衝區默認爲1MB,對於大流量場景顯然不夠,這時需要增大積壓緩衝區,保證repl_backlog_size>net_break_time*write_size_per_minute,從而避免因複製積壓緩衝區不足造成的全量複製。

(3)複製緩衝區不足:

在全量複製階段,主節點會將執行的寫命令放到複製緩衝區中,該緩衝區存放的數據包括了以下幾個時間段內主節點執行的寫命令:bgsave生成RDB文件、RDB文件由主節點發往從節點、從節點清空老數據並載入RDB文件中的數據。當主節點數據量較大,或者主從節點之間網絡延遲較大時,可能導致該緩衝區的大小超過了限制,此時主節點會斷開與從節點之間的連接;這種情況可能引起全量複製->複製緩衝區溢出導致連接中斷->重連->全量複製->複製緩衝區溢出導致連接中斷……的循環。

複製緩衝區的大小由client-output-buffer-limit slave {hard limit} {soft limit} {soft seconds}配置,默認值爲client-output-buffer-limit slave 256MB 64MB 60,其含義是:如果buffer大於256MB,或者連續60s大於64MB,則主節點會斷開與該從節點的連接。該參數是可以通過config set命令動態配置的(即不重啓Redis也可以生效)。

(4)主重啓後的runId改變:

這時可以使用debug reload命令重新加載RDB並保持運行ID不變,從而有效避免不必要的全量複製(debug reload命令會阻塞當前Redis節點主線程,阻塞期間會生成本地RDB快照並清空數據之後再加載RDB文件。因此對於大數據量的主節點和無法容忍阻塞的應用場景,謹慎使用。)


複製相關的配置

這一節總結一下與複製有關的配置,說明這些配置的作用、起作用的階段,以及配置方法等;通過了解這些配置,一方面加深對Redis複製的瞭解,另一方面掌握這些配置的方法,可以優化Redis的使用,少走坑。

配置大致可以分爲主節點相關配置、從節點相關配置以及與主從節點都有關的配置,下面分別說明。

(1)與主從節點都有關的配置

首先介紹最特殊的配置,它決定了該節點是主節點還是從節點:

1)   slaveof <masterip> <masterport>:Redis啓動時起作用;作用是建立複製關係,開啓了該配置的Redis服務器在啓動後成爲從節點。該註釋默認註釋掉,即Redis服務器默認都是主節點。

2)   repl-timeout 60:與各個階段主從節點連接超時判斷有關,見前面的介紹。

(2)主節點相關配置

1)   repl-diskless-sync no:作用於全量複製階段,控制主節點是否使用diskless複製(無盤複製)。所謂diskless複製,是指在全量複製時,主節點不再先把數據寫入RDB文件,而是直接寫入slave的socket中,整個過程中不涉及硬盤;diskless複製在磁盤IO很慢而網速很快時更有優勢。需要注意的是,截至Redis3.0,diskless複製處於實驗階段,默認是關閉的。

2)   repl-diskless-sync-delay 5:該配置作用於全量複製階段,當主節點使用diskless複製時,該配置決定主節點向從節點發送之前停頓的時間,單位是秒;只有當diskless複製打開時有效,默認5s。之所以設置停頓時間,是基於以下兩個考慮:(1)向slave的socket的傳輸一旦開始,新連接的slave只能等待當前數據傳輸結束,才能開始新的數據傳輸 (2)多個從節點有較大的概率在短時間內建立主從複製。

3)   client-output-buffer-limit slave 256MB 64MB 60:與全量複製階段主節點的緩衝區大小有關,見前面的介紹。

4)   repl-disable-tcp-nodelay no:開啓後會合併小的tcp請求(延遲增大,降低帶寬消耗)。

5)   masterauth <master-password>:與連接建立階段的身份驗證有關,見前面的介紹。

6)   repl-ping-slave-period 10:與命令傳播階段主從節點的超時判斷有關,見前面的介紹。

7)   repl-backlog-size 1mb:複製積壓緩衝區的大小,見前面的介紹。

8)   repl-backlog-ttl 3600:當主節點沒有從節點時,複製積壓緩衝區保留的時間,這樣當斷開的從節點重新連進來時,可以進行全量複製;默認3600s。如果設置爲0,則永遠不會釋放複製積壓緩衝區。

9)   min-slaves-to-write 3與min-slaves-max-lag 10:規定了主節點的最小從節點數目,及對應的最大延遲,見前面的介紹。

(3)從節點相關配置

1)   slave-serve-stale-data yes:與從節點數據陳舊時是否響應客戶端命令有關,見前面的介紹。

2)   slave-read-only yes:從節點是否只讀;默認是隻讀的。由於從節點開啓寫操作容易導致主從節點的數據不一致,因此該配置儘量不要修改。


來個腦圖


評論吧  大哥們

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章