redis系列:主從複製

HitTwice發表於2018-08-10

1 簡介

這篇文章主要講述Redis的主從複製功能。會依次從環境搭建、功能測試和原理分析幾個方面進行介紹。

2 準備工作

伺服器架構圖如下

redis系列:主從複製

啟動主伺服器101,使用info replication命令檢視狀態,可以看到role為master(也就是角色為主主伺服器),connected_salaves的值為0(從伺服器數量為0)

redis系列:主從複製

接下來用修改配置檔案的方式將102機器加入的主從複製當中

然後再用命令的方式同樣將103機器加入的主從複製當中。

2.1 用修改配置檔案的方式將102機器加入到主從

ip地址為192.168.17.102的機器的Redis配置檔案增加slaveof 192.168.17.101 6379
啟動102的redis,狀態如下

redis系列:主從複製

可以看到role變為slave(角色為從伺服器),master_host(主伺服器IP地址)為192.168.17.101,master_port(主伺服器埠)為6379。
此時101主伺服器的主從狀態如下,可以看到connected_salaves的值變為1,以及增加了一行slave0(從伺服器的狀態)

redis系列:主從複製

2.2 用命令的方式將103機器加入到主從

未執行slaveof命令的主從狀態如下

redis系列:主從複製

開始執行slaveof命令

192.168.17.103:6379> slaveof 192.168.17.101 6379
OK
複製程式碼

再次檢視狀態,可以看到角色已經變成從伺服器

redis系列:主從複製

現在再來看看主伺服器的狀態,可以看到從伺服器數量變成2,又多了一條從伺服器的資訊

redis系列:主從複製

到這裡主從環境就搭好了,現在來測試一波

2.3 測試

現在主伺服器101輸入命令

192.168.17.101:6379> set 101 101
OK
複製程式碼

然後在從伺服器102上檢視所有的鍵,發現有鍵101,接著設定鍵102

192.168.17.102:6379> keys *
1) "101"
192.168.17.102:6379> get 101
"101"
192.168.17.102:6379> set 102 102
(error) READONLY You can't write against a read only slave.
複製程式碼

發現出現錯誤(error) READONLY You can't write against a read only slave. 後面在講述出錯原因

現在在從伺服器103上檢視所有的鍵,發現也有101

192.168.17.103:6379> keys *
1) "101"
複製程式碼

再向主伺服器101輸入命令

192.168.17.101:6379> set ip ip
OK
複製程式碼

然後到從伺服器103上檢視所有的鍵

192.168.17.103:6379> keys *
1) "101"
2) "ip"
複製程式碼

可以看到多了一個鍵,說明主服務的資料同步到了從伺服器上,操作過程看下圖

redis系列:主從複製

2.4 其他

2.4.1 (error) READONLY You can't write against a read only slave.

出現錯誤(error) READONLY You can't write against a read only slave. 是因為 從節點預設是隻讀的,如需修改可以再配置檔案中修改下面這個屬性

slave-read-only yes
複製程式碼

2.4.2 主伺服器設定密碼

當主服務設定密碼時,配置檔案需要增加如需引數

masterauth <master-password>
複製程式碼

3 實現原理

當我在從伺服器103上輸入slaveof命令時,出現如下日誌

redis系列:主從複製

總的來說主從複製功能的詳細步驟可以分為7個步驟:

  1. 設定主伺服器的地址和埠
  2. 建立套接字連線
  3. 傳送PING命令
  4. 身份驗證
  5. 傳送埠資訊
  6. 同步
  7. 命令傳播

接下來分別敘述每個步驟

3.1設定主伺服器的地址和埠

主從複製的第一步就是設定主伺服器的地址和埠,當輸入slaveof命令或者在配置檔案中配置資訊時,從伺服器會將主伺服器的ip地址和埠號儲存到伺服器狀態的屬性裡面。

3.2 建立套接字連線

在slaveof命令執行之後,從伺服器會根據設定的ip和埠,向主伺服器簡歷socket連線。

3.3 傳送PING命令

socket連線成功後,從伺服器會傳送一PING命令給主伺服器。

這時候PING命令可以檢查socket的讀寫狀態是否正常,還可以檢查主伺服器能否正常處理命令請求。

從伺服器在傳送PING命令時可能遇上的情況如下圖

圖片來自Redis設計與實現

3.4 身份驗證

從伺服器收到主伺服器的PONG回覆後,會檢查從伺服器是否設定masterauth,設定則進行身份驗證,未設定則跳過該步驟。從伺服器在身份驗證時可能遇上的情況如下

圖片來自Redis設計與實現

3.5 傳送埠資訊

身份驗證通過後,從伺服器會向主伺服器傳送自己的監聽埠號。主伺服器收到之後會將埠號記錄到從伺服器對應的狀態屬性中。在主伺服器呼叫info replication可以看到從伺服器的port,如下

redis系列:主從複製

3.6 同步

傳送埠資訊之後,從伺服器會向主伺服器傳送PSYNC命令,執行同步操作,並將自己的資料庫同步至主伺服器資料庫當前的狀態。

同步這塊內容會在後面詳細描述

3.7 命令傳播

當完成同步操作之後,主從伺服器便會進入命令傳播階段。這時候主從伺服器的資料是一致的,當主伺服器有新的寫命令時,會將改命令傳送給從伺服器,從伺服器接收命令並執行便可以保證與主伺服器的資料保持一致。
那麼Redis是如何保證主從伺服器一致處於連線狀態以及命令是否丟失?
答:命令傳播階段,從伺服器會利用心跳檢測機制定時的向主服務傳送訊息。
從伺服器傳送的命令如下

REPLCONF ACK <replication_offset>
複製程式碼

replication_offset表示從伺服器當前的複製偏移量
接下來看看心跳機制

3.7.1 心跳檢測機制

心跳檢測機制的作用有三個:

  1. 檢查主從伺服器的網路連線狀態
  2. 輔助實現min-slaves選項
  3. 檢測命令丟失

3.7.1.1 檢查主從伺服器的網路連線狀態

主伺服器資訊中可以看到所屬的從伺服器的連線資訊,state表示從伺服器狀態,offset表示複製偏移量,lag表示延遲值(幾秒之前有過心跳檢測機制)

redis系列:主從複製

3.7.1.2 輔助實現min-slaves選項

Redis.conf配置檔案中有下方兩個引數

# 未達到下面兩個條件時,寫操作就不會被執行
# 最少包含的從伺服器
# min-slaves-to-write 3
# 延遲值
# min-slaves-max-lag 10
複製程式碼

如果將兩個引數的註釋取消,那麼如果從伺服器的數量少於3個,或者三個從伺服器的延遲(lag)大於等於10秒時,主伺服器都會拒絕執行寫命令。

3.7.1.3 檢測命令丟失

在從伺服器的連線資訊中可以看到複製偏移量,如果此時主伺服器的複製偏移量與從伺服器的複製偏移量不一致時,主伺服器會補發缺失的資料。

4 同步原理

同步分為全量重同步和部分重同步。那麼是什麼決定採取全量重同步還是部分重同步操作?

圖片來自Redis設計與實現

4.1 全量重同步

全量重同步的步驟如下

  1. 主節點收到從伺服器的全量重同步請求時,主伺服器便開始執行bgsave命令,同時用一個緩衝區記錄從現在開始執行的所有寫命令。
  2. 當主伺服器的bgsave命令執行完畢後,會將生成的RDB檔案傳送給從伺服器。從伺服器接收到RDB檔案時,會將資料檔案儲存到硬碟,然後載入到記憶體中。
  3. 主伺服器將緩衝區所有快取的命令傳送到從伺服器,從伺服器接收並執行這些命令,將從伺服器同步至主伺服器相同的狀態。

4.2 部分重同步

要想了解部分重同步的步驟,需要先了解部分重同步所需要的幾個屬性

  1. 複製偏移量
  2. 複製緩衝區
  3. 執行ID

4.2.1 複製偏移量

從主伺服器的複製資訊可以看到從伺服器slave0和slave1都有一個引數offset,這個引數就是從伺服器的複製偏移量。master_repl_offset這個引數就是主伺服器的偏移量。如下圖

redis系列:主從複製

主伺服器的複製偏移量儲存向從伺服器傳送過的位元組資料。
從伺服器的複製偏移量儲存著從主伺服器接收的位元組資料。
通過對比主伺服器和從伺服器的複製偏移量就可以知道命令是否丟失,丟失則補發複製偏移量相差的位元組命令。
那麼這些位元組資料是存放在哪裡的呢?

4.2.2 複製緩衝區

這些位元組資料都是存放在主伺服器的複製緩衝區裡的。複製緩衝區是一個固定長度(fixed-size)先進先出(FIFO)的佇列,預設大小為1MB。預設大小可以對下方的引數進行修改

# repl-backlog-size 1mb
複製程式碼

那麼複製緩衝區的資料是什麼時候加入進去的呢?

答:在命令傳播階段,主節點除了將寫命令傳送給從節點,還會傳送一份給複製積壓緩衝區。

redis系列:主從複製

複製緩衝區裡面會儲存著一部分最傳播的寫命令和每個位元組相應的複製偏移量。

由於複製緩衝區的大小是有限制的,所以儲存的資料也是有限制的。如果從伺服器與主伺服器的複製偏移量相差的資料大於複製緩衝去儲存的資料時,同樣不會執行部分重同步。

舉個例子,主伺服器的複製偏移量為20000、緩衝區能儲存的資料只有5000,從伺服器的複製偏移量為10000。這時從伺服器與主伺服器複製偏移量10000,而緩衝區只有5000,那麼還是會執行全量重同步。如果相差的複製偏移量小於5000,才會執行部分重同步。

4.2.3 執行ID

每個Redis伺服器啟動時,都會有自動生成自己的執行ID。
當從伺服器對主伺服器進行初次複製時,主伺服器會傳送自己的執行ID給從伺服器。
當從伺服器斷線重連時,會將之前主伺服器的執行ID傳送給當前連線的主伺服器。這時候會出現下面兩種情況

  1. 執行ID和主伺服器一致,主伺服器可以嘗試執行部分重同步操作。
  2. 執行ID和主伺服器不一致,說明之前連線的主伺服器與這次連線不同,開始執行全量重同步操作。

5 相關配置

################################# REPLICATION #################################

# slaveof <主伺服器ip> <主伺服器埠>
# slaveof <masterip> <masterport>

# masterauth <主伺服器Redis密碼>
# masterauth <master-password>

# 當slave丟失master或者同步正在進行時,如果發生對slave的服務請求
# yes則slave依然正常提供服務
# no則slave返回client錯誤:"SYNC with master in progress"
slave-serve-stale-data yes

# 指定slave是否只讀
slave-read-only yes

# 無硬碟複製功能
repl-diskless-sync no

# 無硬碟複製功能間隔時間
repl-diskless-sync-delay 5

# 從伺服器傳送PING命令給主伺服器的週期
# repl-ping-slave-period 10

# 超時時間
# repl-timeout 60

# 是否禁用socket的NO_DELAY選項
repl-disable-tcp-nodelay no

# 設定主從複製容量大小,這個backlog 是一個用來在 slaves 被斷開連線時存放 slave 資料的 buffer
# repl-backlog-size 1mb

# master 不再連線 slave時backlog的存活時間。
# repl-backlog-ttl 3600

# slave的優先順序
slave-priority 100

# 未達到下面兩個條件時,寫操作就不會被執行
# 最少包含的從伺服器
# min-slaves-to-write 3
# 延遲值
# min-slaves-max-lag 10
複製程式碼

結語

主從的配置檔案:[https://github.com/rainbowda/learnWay/tree/master/learnRedis/replication,有需要可以下載。

Redis的主從複製功能就介紹到這裡了。雖然說主從解決了讀寫分離,讀資料的負載均衡,但是一旦某個節點出現故障,不能自動回覆,主從切換等功能。所以就有了哨兵的功能。

相關文章