全網最詳細解讀:Redis主從模式搭建及應用

在高併發服務當中,如果使用單個Redis實例,由於Redis採用單進程單線程處理所有請求的方式,即每次只有一個請求在處理,後面的請求排隊,如果前面請求執行時間長了,則會影響後面所有請求。所以可以拓展到多個Redis實例,採用主從機制,一個master和多個slave,master和多個slave包含相同的數據,master負責處理寫請求,slave負責讀請求。Redis主從同步即是實現這種機制的,master處理完寫請求後,同步給多個slave,從而保證數據的最終一致性。

實現方式

Redis的主從複製有多種實現方式:

全網最詳細解讀:Redis主從模式搭建及應用


上圖表示的是一臺 Master 服務器與 Slave 服務器的情況,其實一臺 Master 服務器也可以對應多臺 Slave 服務器,如下圖所示:

全網最詳細解讀:Redis主從模式搭建及應用


另外,Slave 服務器也可以有自己的 Slave 服務器,如下圖所示:

全網最詳細解讀:Redis主從模式搭建及應用


在 Redis 2.6 以後,Slave 只讀模式是默認開啟的,我們可以通過配置文件中的 slave-read-only 選項配置是否開啟只讀模式:

slave-read-only yes 

配置

配置某個redis使用另外一個redis作為master也很簡單,redis.conf的配置如圖:

################################# REPLICATION #################################
# 配置master服務器IP和端口
# slaveof <masterip> <masterport>
# 配置master服務器訪問密碼
# masterauth <master-password>
/<master-password>/<masterport>/<masterip>

另外,還可以通過redis客服端中執行slaveof {masterip} {masterport}來配置Master服務器。

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

slaveof no one

全量同步

slave啟動或者slave斷開重連master的時候,slave會發生SYNC命令給master,master接收到該命令後,則會通過bgsave啟動一個子進程將當前時間點的master全部數據的快照寫到一個文件中,然後發送給slave,slave接收到之後則清空內存,載入這些數據,步驟如下:

  • slave發生SYNC命令給master,請求執行全量同步,然後slave在等待期間,根據配置(如下)可以使用舊數據響應客戶端請求或者直接報錯;
# directive below) it is possible to tell the slave to authenticate before
# refuse the slave request.
# When a slave loses its connection with the master, or when the replication
# is still in progress, the slave can act in two different ways:
# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will
# 2) if slave-serve-stale-data is set to 'no' the slave will reply with
slave-serve-stale-data yes
  • master接收到SYNC請求後,通過BGSAVE啟動一個子進程將當前數據快照寫到一個快照文件;同時主進程採用一個緩衝區保存這段時間內的所有寫請求;注意如果同時有多個slave發送SYNC給master,master只會執行一次BGSAVE,然後將快照文件發送給這些slaves;
  • 子進程完成快照文件的寫入,向slave發送這個快照文件(正常邏輯來說是子進程發送待查看源代碼確認);主進程繼續採用緩衝區緩存寫命令;slave接收到快照文件後,刪除舊數據,載入新的快照數據,此期間會阻塞客戶端的請求;
  • 子進程發送快照文件完畢後,主進程將緩衝區的數據以Redis命令協議形式,如DEL、SET,發送給slave。slave載入完快照數據後,則可以開始處理客戶端的請求了。同時slave接收master發送過來的緩衝區的寫命令並執行;
  • 此後進入增量同步(命令傳播)模式,不斷接收master的寫請求,實現最終一致性。

以上步驟,可以通過redis日誌驗證:

22221:M 02 Sep 10:12:23.489 * The server is now ready to accept connections on port 6379
22221:M 02 Sep 10:13:41.537 * Slave 192.168.1.107:6379 asks for synchronization
22221:M 02 Sep 10:13:41.537 * Full resync requested by slave 192.168.1.107:6379
22221:M 02 Sep 10:13:41.537 * Starting BGSAVE for SYNC with target: disk
22221:M 02 Sep 10:13:41.538 * Background saving started by pid 22234
22234:C 02 Sep 10:13:41.544 * DB saved on disk
22234:C 02 Sep 10:13:41.544 * RDB: 0 MB of memory used by copy-on-write
22221:M 02 Sep 10:13:41.627 * Background saving terminated with success
22221:M 02 Sep 10:13:41.628 * Synchronization with slave 192.168.1.107:6379 succeeded

增量同步

增量同步即為master每接收並執行一個寫命令都同步給所有的slave,slave接收到該寫命令後執行對自身數據的修改從而保持與master的數據一致。

部分同步(PSYNC)

在Redis2.8+版本,Redis的slave在與master斷開連接重連的時候,默認是使用新的PSYNC同步方法,而不是原來的SYNC,因為斷線重連時,slave是包含有數據的,只是可能落後於master,所以沒必要又進行一次全量同步。PSYNC命令如下:

PSYNC <runid> <offset> # runid:主服務器ID, offset:從服務器最後接收命令的偏移量 

/<offset>/<runid>

流程說明

全網最詳細解讀:Redis主從模式搭建及應用


從節點發送 psync 命令給主節點,runId 就是目標主節點的 ID,如果沒有默認為 -1,offset 是從節點保存的複製偏移量,如果是第一次複製則為 -1.

主節點會根據 runid 和 offset 決定返回結果:

  • 如果回覆 +FULLRESYNC {runId} {offset} ,那麼從節點將觸發全量複製流程。
  • 如果回覆 +CONTINUE,從節點將觸發部分複製。
  • 如果回覆 +ERR,說明主節點不支持 2.8 的 psync 命令,將使用 sync 執行全量複製。

主服務器ID

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

192.168.1.106:6379> info server
# Server
...
run_id:2d31f8a381669bc2327a5fb07902c4341e3f1d11
tcp_port:6379

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

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

複製偏移量

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

192.168.1.106:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.1.107,port=6379,state=online,offset=1121,lag=1
master_repl_offset:1121
repl_backlog_active:1 # 開啟複製積壓緩衝區
repl_backlog_size:1048576 # 緩衝區最大長度
repl_backlog_first_byte_offset:2 # 起始偏移量,計算當前緩衝區可用範圍
repl_backlog_histlen:1120 # 已保存數據的有效長度

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

192.168.1.107:6379> info replication
# Replication
role:slave
master_host:192.168.1.106
master_port:6379
master_link_status:up
master_last_io_seconds_ago:8
master_sync_in_progress:0
slave_repl_offset:1121
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0

repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

從節點向主節點默認每隔1秒發送replconf ack {offset}命令,把自身的複製偏移量上報給主節點,主節點會保存這個從節點的複製偏移量。

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

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

複製積壓緩衝區

複製積壓緩衝區是主服務器維護的一個固定長度,先進先出的隊列,默認為1M大小。當主節點有連接的從節點時被創建,主節點將命令發送給從節點時,還會寫入複製積壓緩衝區,作為寫命令的備份,並且會為隊列裡的每個字節記錄相應的複製偏移量。

心跳機制

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

全網最詳細解讀:Redis主從模式搭建及應用


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

心跳的關鍵機制如下:

  • 主從都有心跳檢測機制,各自模擬成對方的客戶端進行通信,通過 client list 命令查看複製相關客戶端信息,主節點的連接狀態為 flags = M,從節點的連接狀態是 flags = S。
  • 主節點默認每隔 10 秒對從節點發送 ping 命令,可修改配置 repl-ping-slave-period 控制發送頻率。
  • 從節點在主線程每隔一秒發送 replconf ack{offset} 命令,給主節點上報自身當前的複製偏移量。
  • 主節點收到 replconf 信息後,判斷從節點超時時間,如果超過 repl-timeout 60 秒,則判斷節點下線。
# Slaves send PINGs to server in a predefined interval. It's possible to change
# this interval with the repl_ping_slave_period option. The default value is 10
# seconds.
#
# repl-ping-slave-period 10
#
# 1) Bulk transfer I/O during SYNC, from the point of view of slave.
# 2) Master timeout from the point of view of slaves (data, pings).
# 3) Slave timeout from the point of view of masters (REPLCONF ACK pings).
#
# It is important to make sure that this value is greater than the value
# specified for repl-ping-slave-period otherwise a timeout will be detected
# every time there is low traffic between the master and the slave.
#
# repl-timeout 60

Key過期問題

我們都知道 Redis 可以通過設置 Key 的過期時間來限制 Key 的生存時間,Redis 處理 Key 過期有惰性刪除和定期刪除兩種機制。

而在配置主從複製後,Slave 服務器就沒有權限處理過期的 Key,這樣的話,對於在 Master 上過期的 Key,在 Slave 服務器就可能被讀取。

所以 Master 會累積過期的 Key,積累一定的量之後,發送 Del 命令到 Slave,刪除 Slave 上的 Key。

如果 Slave 服務器升級為 Master 服務器 ,則它將開始獨立地計算 Key 過期時間,而不需要通過 Master 服務器的幫助。

CAP理論

CAP理論為:在分佈式系統中,多個節點之間只能滿足CP/AP,即強一致性和高可用是不能同時滿足的。Redis的主從同步是AP的,具體對高可用的強度要求,可用通過在redis.conf配置,即有至少有多少個slaves存在和至多多少秒內沒響應,則才執行寫請求,否則報錯,配置與說明如圖:默認為關閉這個特性,即master始終接收客戶端寫請求。

# min-slaves-to-write 3
# min-slaves-max-lag 10


分享到:


相關文章: