1. 程式人生 > >一文了解:Redis主從複製

一文了解:Redis主從複製

Redis主從複製

主從複製

主從複製,將一臺Redis伺服器的資料,複製到其他Redis伺服器。前者稱為主(master)節點,後者稱為從(slave)節點 。
在預設的情況下,Redis都是主節點,每個從節點只能有一個主節點,一個主節點可以有多個從節點。複製的資料只能從主節點複製到從節點。

配置方式

  1. 在從節點的配置檔案中配置:slaveof {masterip} {masterport}
  2. 在redis-server啟動命令後加入:--slaveof {masterip} {masterport}
  3. 在redis客戶端使用命令:slaveof {masterip} {masterport}

建立主從關係

啟動兩個例項

//例項一:預設埠6379
./redis-server

//例項二:修改埠為6380
./redis-server --port 6380

結果如下圖

建立主從連線

// 把埠為6380的redis-server掛在6379的redis-server下
./redis-cli -p 6380
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
主節點新增key
127.0.0.1:6379} set masterKey 'This is master Key'
OK
從節點查詢key
127.0.0.1:6380> get masterKey
"This is master Key"

可以發現主節點的Key已經同步到從節點了

主節點刪除key
127.0.0.1:6379} del masterKey
(integer) 1
從節點再次查詢key
127.0.0.1:6380> get masterKey
(nil)

可以發現從節點的key也已經被刪除了

斷開連線

通過slaveof {masterip} {masterport}命令建立主從複製關係以後,可以通過slaveof no one斷開。從節點斷開復制後,不會刪除已有的資料,只是不再接受主節點新的資料變化。

使用命令slaveof no one
127.0.0.1:6380> slaveof no one
OK

原理

在從節點執行slaveof命令後,主從複製的過程就開始了,可以分為6個步驟:

儲存主節點資訊

//從節點的redis-server中日誌
27604:S 21 Aug 22:38:56.934 * SLAVE OF 127.0.0.1:6379 enabled (user request from 'id=3 addr=127.0.0.1:60092 fd=8 name= age=69 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')

從上面的日誌中可以看到salveof中的masterhost和masterport都被儲存了下來。

建立連線

//從節點的redis-server中日誌
27604:S 21 Aug 22:38:57.444 * Connecting to MASTER 127.0.0.1:6379
27604:S 21 Aug 22:38:57.445 * MASTER {-} SLAVE sync started

從節點內部會使用一個每秒執行的定時任務,當發現了新的主節點後根據主節點的host和port建立一個socket連線。

當連線失敗的時候定時任務會無限重試連線直到連線成功或者使用slaveof on one命令取消主從連線。

//從節點的redis-server中日誌
27639:S 21 Aug 22:50:02.825 # Error condition on socket for SYNC: Connection refused

傳送ping命令

連線建立成功後從節點(salve)傳送ping命令進行首次通訊,主要目的是:

  • 檢查主從之間網路套接字是否可用
  • 檢查主節點當前是否可接受處理命令

從節點發送ping命令後會收到主節點的pong回覆或者是超時問題,從節點便會斷開連線,下次定時任務時再次傳送ping命令

許可權驗證

如果主節點設定了requierpass引數,則從節點必須配置masterauth引數進行密碼驗證。

從節點會向主節點發送auth命令進行驗證,auth引數為masterauth引數的值。如果驗證沒通過從節點會斷開連線,並重連

從節點發送埠資訊

身份驗證之後,從節點會向主節點發送其監聽的埠號,主節點將該資訊儲存到該從節點對應的客戶端的slave_listening_port欄位中

資料同步

主從複製能正常通訊後,首次建立連線後主節點會把全部資料傳送給從節點,相當於從節點完成資料初始化工作。

同步的方式有全量同步和部分同步。

命令複製

當主節點把所有資料複製給從節點後,主節點會把命令傳輸給從節點,從節點接收到命令後執行,以保證資料完整性

資料同步方式

Redis資料同步方式有全量同步和部分同步,Redis使用psync命令進行主從同步。

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

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

複製偏移量

主節點和從節點都會維護自身複製偏移量(offset),主節點在處理完命令後,會將命令的位元組長度做累加並記錄,統計在info replication中的master_repl_offset中。

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

從節點在接收到主節點發送的命令後,同樣累計記錄自身的偏移量,統計在info replication中的slave_repl_offset中。

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

從節點每秒鐘把自身的複製偏移量上報給主節點,主節點會儲存這個從節點的複製偏移量。記錄在從節點對應的ip,port行的offset中

127.0.0.1:6379> info replication
# Replication
role:master
...
slave0:ip=127.0.0.1,port=6380,state=online,offset=308,lag=1

複製積壓緩衝區

複製積壓緩衝區是主伺服器維護的一個固定長度,先進先出的佇列,預設為1M大小。當主節點有連線的從節點時被建立,主節點將命令傳送給從節點時,還會寫入複製積壓緩衝區,作為寫命令的備份,並且會為佇列裡的每個位元組記錄相應的複製偏移量。

複製積壓緩衝區的一些資料儲存在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:1        # 起始偏移量,計算當前緩衝區可用範圍
repl_backlog_histlen:308                # 已儲存資料的有效長度

主節點執行ID

每個Redis節點在啟動後都會動態分配一個唯一的40位十六進位制字串作為執行ID(run_id)。當Redis重啟後,執行ID也會改變。

127.0.0.1:6379> info server
# Server
...
run_id:9cc202d7825028c28e91207452e993de8cdb145c
tcp_port:6379
...

當主從節點第一次複製的時候,主節點會將run_id傳送給從節點,從節點斷線重新連線的時候,從節點將run_id傳送給主節點,主節點和當前的自身的run_id判斷是否需要全量複製。

  1. 當從節點發送run_id和主節點當前的run_id不相同,說明從節點在斷線前和斷線後的主節點不相同,需要全量複製
  2. 當從節點發送run_id和主節點當前的run_id相同,主節點根據複製偏移量和複製積壓緩衝區判斷是需要全量複製還是部分複製

psync命令

從節點使用psync {run_id} {offset}命令完成全量複製或者部分複製

  1. run_id:從節點儲存的主節點run_id
  2. offset:從節點的複製偏移量


(psync執行流程, 圖片來自《Redis設計與實現》)

從節點向主節點發送命令

  • 從節點從未執行過slaveof或者最近一次執行了slaveof no one,從節點向主節點發送psync ? -1請求全量複製。
  • 從節點執行過slaveof,從節點向主節點發送psync {run_id} {offset}命令,主節點判斷是否需要全量複製。

主節點判斷是否需要全量複製

  • 主節點根據pysnc引數和自身伺服器狀態,判斷是全量複製還是部分複製
  • 如果主節點的Redis版本小於2.8,則返回+ERR,從節點發送重新sync命令觸發全量複製
  • 如果主節點的run_id和psync命令中run_id引數相同,且命令中的offset引數之後的資料都存在複製積壓緩衝區,則返回+CONTINUE,從節點等待主節點的部分複製
  • 如果主節點的run_id和psync命令中run_id引數不同,或者命令中的offset引數之後的資料有部分不再複製積壓緩衝區中,則返回+FULLRESYNC {run_id} {offset},從節點觸發全量複製,並且儲存主節點的run_id和offset

心跳機制

主從複製建立之後,主從節點之間會維護兩個心跳機制

PING

主節點向從節點預設每隔10秒傳送PING命令,判斷從節點是否存活和連線狀態。配置引數repl-ping-slave-period可以控制PING命令的頻率。

REPLCONF ACK

從節點向主節點預設每隔1秒傳送replconf ack {offset}命令。它的作用是:

實時監測主從節點網路狀態
127.0.0.1:6379> info replication
# Replication
role:master
...
slave0:ip=127.0.0.1,port=6380,state=online,offset=308,lag=1

在主節點的info replication中可以看到lag=1,表示主節點上次收到replconf ack命令的間隔,正常情況下應該為0或者1

上報自身的偏移量

從節點上報自身偏移量判斷是否丟失資料,主節點把自身的offset和從節點的offset,如果從節點丟失資料,主節點會推送資料給從節點,如果從節點的offset之後的資料不在複製積壓緩衝區中,則需要全量複製否則為部分複製。

實現保證從節點的數量和延遲功能

主節點中使用min-slaves-to-write(預設3個)和min-slaves-max-lag(預設10s)引數,保證從節點小於3個或所有從節點延遲大於10秒,主節點拒絕執行寫命令。從節點的延遲資料是通過replconf ack命令的時間判斷的,儲存在info replication中的lag資訊中。如果超過repl-timeout(預設60s)配置的值,則判斷從節點下線並斷開復制連線