Redis主從同步及哨兵原理


 

1、復制過程

  復制過程大致分為6個過程:

  流程圖如下:

  

  1)保存主節點信息

    執行slaveof后從節點只保存主節點的地址信息便直接返回,這時建立復制流程還沒有開始,在從節點執行info replication可以看到如下信息:

master_host:xxx
master_port:xxx
master_link_status:down

    從統計信息可以看出,主節點的ip和port被保存下來,但是主節點的連接狀態master_link_status是下線狀態。執行slaveof后Redis會打印如下日志:

SLAVE OF 127.0.0.1:6379 enabled (user request from 'id=65 addr=127.0.0.1:58090
fd=5 name= age=11 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=
32768 obl=0 oll=0 omem=0 events=r cmd=slaveof')

  2)從節點內部通過每秒運行的定時任務維護復制相關邏輯,當定時任務發現存在新的主節點后,會嘗試與該節點建立網絡連接。從節點會建立一個socket套接字,專門用於接受主節點發送的復制命令。如連接成功,打印日志如下:

* Connecting to MASTER 127.0.0.1:6379
* MASTER <-> SLAVE sync started

    如果從節點無法建立連接,定時任務會無限重試直到連接成功或執行slaveof no one取消復制。

    連接失敗,可以在從節點執行info replication查看master_link_down_since_seconds指標,它會記錄與主節點連接失敗的系統時間。此時日志如下

# Error condition on socket for SYNC: {socket_error_reason}

  3) 發送ping命令

    連接建立成功后從節點發送ping請求進行首次通信,ping請求主要目的如下:

    - 檢測主從之間網絡套接字是否可用

    - 檢測主節點當前是否可接受處理命令

    如果發送ping命令后,從節點沒有收到主節點的pong回復或者超時,比如網絡超時或者主節點正在阻塞無法響應命令,從節點會斷開復制鏈接,下次定時任務時任務會重連。

    從節點與主節點建立連接流程如圖

    

    從節點發送ping命令流程

    

  4)權限驗證

    如果主節點設置了requirepass參數,則需要密碼驗證,從節點必須配置masterauth參數保證與主節點相同的密碼才能通過驗證;如果驗證失敗復制將終止,從節點重新發起復制流程。

  5)同步數據集

    主從復制鏈接正常通信后,對於首次建立復制的場景,主節點會把持有的數據全部發送給從節點,這部分是耗時最長的步驟。Redis在2.8版本以后采用新復制命令psync進行數據同步,原來的sync命令也同樣支持,為了新舊版本的兼容性。新同步划分為兩種情況:全量同步和部分同步。

  6)命令持續復制

    當主節點把當前數據同步給從節點或,便完成了復制的建立流程,接下來主節點會持續地把寫命令發送給從節點,保證主從數據一致性。

 

2、數據同步

  Redis在2.8及以上版本使用psync命令完成主從數據同步,同步過程分為全量復制和部分復制。

  全量復制:一般用於初次復制場景,Redis早期支持的復制功能只有全量復制,它會把從節點全部數據一次性發送給從節點,當數據較大時,會對主從節點和網絡造成很大的開銷。

  部分復制:用於處理在主從復制中因網絡閃斷等原因造成的數據丟失場景,當從節點再次連上主節點后,如果條件允許,主節點會補發丟失數據給從節點。因為補發的數據遠遠小於全量數據,可以有效避免全量復制的開銷。

  部分復制是對老版復制的重大優化,有效避免了不必要的全量復制操作。因此當使用復制功能時,盡量采用2.8以上版本的Redis。

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

  - 主從節點各自復制偏移量

  - 主節點復制積壓緩沖區

  - 主節點運行id

  1)復制偏移量

    參與復制的主從節點都會維護自身復制偏移量,主節點處理完寫入命令后,會把命令的字節長度做累加記錄,統計信息在info replication中的master_repl_offset指標中。

    從節點每秒上報自身的復制偏移量給主節點,因此主節點也會保存從節點的復制偏移量。均可以通過info replication查看。

127.0.0.1:6379> info replication # Replication role:master ... master_repl_offset:1055130

------------------------------------

127.0.0.1:6379> info replication connected_slaves:1 slave0:ip=127.0.0.1,port=6380,state=online,offset=1055214,lag=1

    從節點在接收到主節點發送的命令后,也會累加記錄自身的偏移量。統計信息在info replication中的slave_repl_offset指標中:

127.0.0.1:6380> info replication # Replication role:slave ... slave_repl_offset:1055214

    通過對比主從節點的復制偏移量,可以判斷主從節點數據是否一致。

    提示:可以通過主節點的統計信息,計算出master_repl_offset - slave_offset字節量,判斷主從節點復制相差的數據量,根據這個差值判定當前復制的健康度。

  2)復制積壓緩沖區

    復制積壓緩沖區是保存在主節點上的一個固定長度的隊列,默認大小為1MB,當主節點有連接的從節點時被創建,這時主節點響應寫命令時,不但會把命令發送給從節點,還會寫入復制積壓緩沖區。

    由於緩沖區本質上是先進先出的定長隊列,所以能實現保存最近已復制數據的功能,用於部分復制和復制命令丟失的數據補救。復制緩沖區相關統計信息也保存在主節點的info replication中:

127.0.0.1:6379> info replication # Replication role:master ... repl_backlog_active:1 // 開啟復制緩沖區
repl_backlog_size:1048576 // 緩沖區最大長度
repl_backlog_first_byte_offset:7479 // 起始偏移量,計算當前緩沖區可用范圍
repl_backlog_histlen:1048576 // 已保存數據的有效長度。

  3)主節點運行ID

    每個Redis節點啟動后都會動態分配一個40位的十六進制字符串作為運行ID。運行ID的主要作用是用來唯一識別Redis節點,比如從節點保存主節點的運行ID識別自己正在復制的是哪個主節點。如果只使用ip+port的方式識別主節點,那么主節點重啟變更了整體數據集(如替換RDB/AOF),從節點再基於偏移量復制數據將是不安全的,因此當運行ID變化后從節點將做全量復制。可以運行info server命令查看當前節點的運行ID:

127.0.0.1:6379> info server # Server redis_version:3.0.7 ... run_id:545f7c76183d0798a327591395b030000ee6def9

    需要注意的是Redis關閉再啟動后,運行ID會隨之改變。Redis不改變ID重啟,可以使用redis-cli debug reload

    注意:debug reload命令會阻塞當前Redis節點主線程,阻塞期間會生成本地RDB快照並清空數據之后再加載RDB文件,因此對於大數據量的節點和無法容忍阻塞的應用場景,謹慎使用。

  4)psync命令

    從節點使用psync命令完成部分復制和全量復制功能,命令格式:psync {runid} {offset}

    - runid:從節點所復制主節點的運行id

    - offset:當前從節點已復制的數據偏移量

    psync運行流程

    

    流程說明:

    1)從節點發送psync命令給主節點,參數runid是當前從節點保存的主節點運行ID,參數offset是當前從節點保存的復制偏移量,如果是第一次復制則為-1。

    2)主節點根據psync參數和自身數據情況決定響應結果:

    - 如果回復 FULLRESYNC {runid} {offset},那么從節點觸發全量復制

    - 如果回復CONTINUE,從節點將觸發部分復制流程

    - 如果回復ERR,說明主節點版本低於2.8,無法識別psync命令,從節點將發送舊版的sync命令觸發全量復制流程。

 

3、全量復制

  全量復制是Redis最早支持的復制方式,也是主從第一次建立復制時必須經歷的階段。觸發全量復制的命令是sync和psync。

  全量復制流程:

  

  流程說明:

  1)發送psync命令進行數據同步,由於是第一次進行復制,從節點沒有復制偏移量和主節點的運行ID,所有發送psync-1

  2)主節點根據psync-1解析出當前為全量復制,回復+FULLRESYNC響應。

  3)從節點接收主節點的響應數據保存運行ID和偏移量offset,執行到當前步驟時從節點打印如下日志:

Partial resynchronization not possible (no cached master) Full resync from master: 92d1cb14ff7ba97816216f7beb839efe036775b2:216789

  4)主節點執行bgsave保存RDB文件到本地,主節點bgsave相關日志如下:

M * Full resync requested by slave 127.0.0.1:6380 M * Starting BGSAVE for SYNC with target: disk C * Background saving started by pid 32618 C * RDB: 0 MB of memory used by copy-on-write M * Background saving terminated with success

  提示:Redis 3.0之后在輸出的日志開頭會有MSC等標識,對應的含義是:M=當前為主節點日志,S=當前為從節點日志,C=子進程日志。

  save和bgsave的區別:save直接調用 rdbSave ,阻塞 Redis 主進程,直到保存完成為止。在主進程阻塞期間,服務器不能處理客戶端的任何請求。BGSAVE 則 fork 出一個子進程,子進程負責調用 rdbSave ,並在保存完成之后向主進程發送信號,通知保存已完成。因為 rdbSave 在子進程被調用,所以 Redis 服務器在BGSAVE 執行期間仍然可以繼續處理客戶端的請求。

  5)主節點發送RDB文件給從節點,從節點把接收的RDB文件保存在本地並直接作為從節點的數據文件,接收完RDB后從節點可以在日志中查看主節點發送的數據量:

16:24:03.057 * MASTER <-> SLAVE sync: receiving 24777842 bytes from master

  注意:對於數據量大的主節點,比如生成的RDB文件超過6GB以上傳輸文件這一步操作非常耗時,速度取決於主從節點之間的網絡帶寬,針對數據量較大的節點,建議調大repl-timeout參數防止出現全量同步數據超時。

  關於無盤復制:為了降低主節點的磁盤開銷,Redis支持無盤復制,生成的RDB文件不保存到硬盤而是直接通過網絡發送給從節點,通過repl-diskless-sync參數控制,默認關閉。無盤復制適用於主節點所在機器磁盤性能較差但網絡帶寬較充裕的場景。注意無盤復制目前依然處於試驗階段。

  6)對於從節點開始接收RDB快照到接收完成期間,主節點仍然響應讀寫命令,因此主節點會把這期間寫命令數據保存在復制客戶端緩沖區內,當從節點加載完RDB文件后,主節點再把緩沖區內的數據發送給從節點,保證主從之間數據一致性。如果主節點創建和傳輸RDB的時間過長,對於高流量寫入場景非常容易造成主節點復制客戶端緩沖區溢出。默認配置為client-output-buffer-limit slave 256MB 64MB 60.如果60秒內緩沖區消耗持續大於64MB或者直接超過356MB時,主節點將直接關閉復制客戶端連接,造成全量同步失敗,日志如下:

M 27 May 12:13:33.669 # Client id=2 addr=127.0.0.1:24555 age=1 idle=1 flags=S qbuf=0 qbuf-free=0 obl=18824 oll=21382 omem=268442640 events=r cmd=psync scheduled to be closed ASAP for overcoming of output buffer limits.

  因此需要根據主節點數據量和寫命令並發量調整client-output-buffer-limit slave配置,避免全量復制期間客戶端緩沖區溢出。

  7)從節點接收完主節點傳送來的全部數據后清空自身舊數據,該步驟對應如下日志:

16:24:02.234 * MASTER <-> SLAVE sync: Flushing old data

  8)從節點清空數據后開始加載RDB文件,對於較大的RDB依然耗時。對於讀寫分離的場景,可以設置slave-serve-stale-data參數為no,關閉命令執行,在完成同步前不響應讀命令。

  9)從節點成功加載完RDB后,如果當前節點開啟了AOF持久化功能,它會立刻做bgrewriteaof操作,為了保證全量復制后AOF持久化文件立刻可用。

  RDB和AOF區別:RDB是將Redis某一時刻的數據持久化到磁盤上,類似於快照;AOF是將redis執行過的所有寫指令記錄下來,在下次redis重新啟動時,只要把這些寫指令從前到后再重復執行一遍,就可以實現數據恢復,默認每秒鍾fsync一次。

 

4、部分復制

  部分復制主要是Redis針對全量復制的過高開銷做出的一種優化措施,使用psync {runid} {offset}命令實現,當從節點正在復制主節點時,如果出現異常情況,從節點要求補發丟失的命令數據,如果主節點的復制積壓緩沖區內存在這部分數據則直接發送給從節點,這樣就可以保持主從節點復制的一致性。

  部分復制過程:

  

  流程說明:

  1)當主從節點之間網絡出現中斷時,如果超過repl-timeout時間,主節點會認為從節點故障並中斷復制鏈接。

  2)主從連接中斷期間主節點依然響應命令,寫命令保存在復制積壓緩沖區,默認最大緩存1MB。

  3)當主從節點網絡恢復后,從節點會再次連接主節點。

  4)連接恢復后,由於從節點保存了自身已復制的偏移量和主節點的運行ID,因此會把他們當做psync參數發送給主節點,要求進行部分復制操作。

  5)主節點接收到psync命令后首先核對參數runid,如果一致,說明之前復制的是當前主節點,之后根據參數offset在自身復制積壓緩沖區查找,找到發送+CONTINUE響應。

  6)主節點根據偏移量把復制積壓緩沖區里的數據發送給從節點,保證主從復制進入正常狀態。

 

5、哨兵模式

  Redis Sentinel通過三個定時監控任務完成對各個節點的發現和控制:

  1)每隔10秒,每個sentinel節點會向主節點和從節點發送info命令獲取最新的拓撲結構。通過解析info replication可以找到相應的從節點。此定時任務作用於三個方面:

  - 通過向主節點執行info命令,獲取從節點的信息。

  - 當有新的節點加入時,可以立刻感知出來。

  - 節點不可達或者故障轉移后,可以通過info命令實時更新節點拓撲信息。

  2)每隔2秒,每隔sentinel節點會向Redis數據節點的__sentinel__:hello頻道上發送該sentinel節點對於主節點的判斷以及當前sentinel節點的信息,同時每隔sentinel節點也會訂閱該頻道,來了解其他sentinel節點以及他們對主節點的判斷,因此此任務主要作用如下:

  - 發現新的sentinel節點

  - sentinel節點之間交換主節點的狀態,作為后面客觀下線以及領導者選舉的依據。

  3)每隔1秒,每隔sentinel節點會向主節點、從節點、其他sentinel節點發送一條ping命令做一次心跳檢測。

 

6、Redis集群

6.1 數據分布

  Redis Cluster把數據集划分到多個節點,每個節點負責整體數據的一個子集。Redis Cluster槽范圍是0~16383,槽是集群內數據管理和遷移的基本單位。采用大范圍槽的主要目的是為了方便數據拆分和集群擴展,每個節點會負責一定數量的槽。

6.2 集群功能限制

  1)key批量操作支持有限。如mset、mget,目前只支持具有相同slot值的key執行批量操作。對於映射為不同slot值的key不被支持。

  2)key事務操作支持有限。同理只支持多key在同一節點上的事務操作,當多個key分布在不同的節點上時無法使用事務功能。

  3)key作為數據分區的最小粒度,因此不能將一個大的鍵值對象如hash、list等映射到不同的節點。

  4)不支持多數據空間。單機支持16個數據庫,集群只有db0。

  5)復制結構只支持一層。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM