深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

零壹技術棧發表於2018-08-22

前言

Redis主從複製 模式下,一旦 主節點 由於故障不能提供服務,需要手動將 從節點 晉升為 主節點,同時還要通知 客戶端 更新 主節點地址,這種故障處理方式從一定程度上是無法接受的。Redis 2.8 以後提供了 Redis Sentinel 哨兵機制 來解決這個問題。

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

其他文章

正文

1. Redis高可用概述

Web 伺服器中,高可用 是指伺服器可以 正常訪問 的時間,衡量的標準是在 多長時間 內可以提供正常服務(99.9%99.99%99.999% 等等)。在 Redis 層面,高可用 的含義要寬泛一些,除了保證提供 正常服務(如 主從分離快速容災技術 等),還需要考慮 資料容量擴充套件資料安全 等等。

Redis 中,實現 高可用 的技術主要包括 持久化複製哨兵叢集,下面簡單說明它們的作用,以及解決了什麼樣的問題:

  • 持久化:持久化是 最簡單的 高可用方法。它的主要作用是 資料備份,即將資料儲存在 硬碟,保證資料不會因程式退出而丟失。

  • 複製:複製是高可用 Redis 的基礎,哨兵叢集 都是在 複製基礎 上實現高可用的。複製主要實現了資料的多機備份以及對於讀操作的負載均衡和簡單的故障恢復。缺陷是故障恢復無法自動化、寫操作無法負載均衡、儲存能力受到單機的限制。

  • 哨兵:在複製的基礎上,哨兵實現了 自動化故障恢復。缺陷是 寫操作 無法 負載均衡儲存能力 受到 單機 的限制。

  • 叢集:通過叢集,Redis 解決了 寫操作 無法 負載均衡 以及 儲存能力 受到 單機限制 的問題,實現了較為 完善高可用方案

2. Redis Sentinel的基本概念

Redis SentinelRedis 高可用 的實現方案。Sentinel 是一個管理多個 Redis 例項的工具,它可以實現對 Redis監控通知自動故障轉移。下面先對 Redis Sentinel基本概念 進行簡單的介紹。

基本名詞說明:

基本名詞 邏輯結構 物理結構
Redis資料節點 主節點和從節點 主節點和從節點的程式
主節點(master) Redis主資料庫 一個獨立的Redis程式
從節點(slave) Redis從資料庫 一個獨立的Redis程式
Sentinel節點 監控Redis資料節點 一個獨立的Sentinel程式
Sentinel節點集合 若干Sentinel節點的抽象組合 若干Sentinel節點程式
Redis Sentinel Redis高可用實現方案 Sentinel節點集合和Redis資料節點程式
應用客戶端 泛指一個或多個客戶端 一個或者多個客戶端程式或者執行緒

如圖所示,Redis主從複製模式Sentinel 高可用架構 的示意圖:

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

3. Redis主從複製的問題

Redis 主從複製 可將 主節點 資料同步給 從節點,從節點此時有兩個作用:

  1. 一旦 主節點當機從節點 作為 主節點備份 可以隨時頂上來。
  2. 擴充套件 主節點讀能力,分擔主節點讀壓力。

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

主從複製 同時存在以下幾個問題:

  1. 一旦 主節點當機從節點 晉升成 主節點,同時需要修改 應用方主節點地址,還需要命令所有 從節點複製 新的主節點,整個過程需要 人工干預

  2. 主節點寫能力 受到 單機的限制

  3. 主節點儲存能力 受到 單機的限制

  4. 原生複製 的弊端在早期的版本中也會比較突出,比如:Redis 複製中斷 後,從節點 會發起 psync。此時如果 同步不成功,則會進行 全量同步主庫 執行 全量備份 的同時,可能會造成毫秒或秒級的 卡頓

4. Redis Sentinel深入探究

4.1. Redis Sentinel的架構

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

4.2. Redis Sentinel的主要功能

Sentinel 的主要功能包括 主節點存活檢測主從執行情況檢測自動故障轉移failover)、主從切換RedisSentinel 最小配置是 一主一從

RedisSentinel 系統可以用來管理多個 Redis 伺服器,該系統可以執行以下四個任務:

  • 監控

Sentinel 會不斷的檢查 主伺服器從伺服器 是否正常執行。

  • 通知

當被監控的某個 Redis 伺服器出現問題,Sentinel 通過 API 指令碼管理員 或者其他的 應用程式 傳送通知。

  • 自動故障轉移

主節點 不能正常工作時,Sentinel 會開始一次 自動的 故障轉移操作,它會將與 失效主節點主從關係 的其中一個 從節點 升級為新的 主節點,並且將其他的 從節點 指向 新的主節點

  • 配置提供者

Redis Sentinel 模式下,客戶端應用 在初始化時連線的是 Sentinel 節點集合,從中獲取 主節點 的資訊。

4.3. 主觀下線和客觀下線

預設情況下,每個 Sentinel 節點會以 每秒一次 的頻率對 Redis 節點和 其它Sentinel 節點傳送 PING 命令,並通過節點的 回覆 來判斷節點是否線上。

  • 主觀下線

主觀下線 適用於所有 主節點從節點。如果在 down-after-milliseconds 毫秒內,Sentinel 沒有收到 目標節點 的有效回覆,則會判定 該節點主觀下線

  • 客觀下線

客觀下線 只適用於 主節點。如果 主節點 出現故障,Sentinel 節點會通過 sentinel is-master-down-by-addr 命令,向其它 Sentinel 節點詢問對該節點的 狀態判斷。如果超過 <quorum> 個數的節點判定 主節點 不可達,則該 Sentinel 節點會判斷 主節點客觀下線

4.4. Sentinel的通訊命令

Sentinel 節點連線一個 Redis 例項的時候,會建立 cmdpub/sub 兩個 連線Sentinel 通過 cmd 連線給 Redis 傳送命令,通過 pub/sub 連線到 Redis 例項上的其他 Sentinel 例項。

SentinelRedis 主節點從節點 互動的命令,主要包括:

命令 作 用
PING SentinelRedis 節點傳送 PING 命令,檢查節點的狀態
INFO SentinelRedis 節點傳送 INFO 命令,獲取它的 從節點資訊
PUBLISH Sentinel 向其監控的 Redis 節點 __sentinel__:hello 這個 channel 釋出 自己的資訊主節點 相關的配置
SUBSCRIBE Sentinel 通過訂閱 Redis 主節點從節點__sentinel__:hello 這個 channnel,獲取正在監控相同服務的其他 Sentinel 節點

SentinelSentinel 互動的命令,主要包括:

命令 作 用
PING Sentinel 向其他 Sentinel 節點傳送 PING 命令,檢查節點的狀態
SENTINEL:is-master-down-by-addr 和其他 Sentinel 協商 主節點 的狀態,如果 主節點 處於 SDOWN 狀態,則投票自動選出新的 主節點

4.5. Redis Sentinel的工作原理

每個 Sentinel 節點都需要 定期執行 以下任務:

  • 每個 Sentinel每秒鐘 一次的頻率,向它所知的 主伺服器從伺服器 以及其他 Sentinel 例項 傳送一個 PING 命令。

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

  1. 如果一個 例項instance)距離 最後一次 有效回覆 PING 命令的時間超過 down-after-milliseconds 所指定的值,那麼這個例項會被 Sentinel 標記為 主觀下線

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

  1. 如果一個 主伺服器 被標記為 主觀下線,那麼正在 監視 這個 主伺服器 的所有 Sentinel 節點,要以 每秒一次 的頻率確認 主伺服器 的確進入了 主觀下線 狀態。

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

  1. 如果一個 主伺服器 被標記為 主觀下線,並且有 足夠數量Sentinel(至少要達到 配置檔案 指定的數量)在指定的 時間範圍 內同意這一判斷,那麼這個 主伺服器 被標記為 客觀下線

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

  1. 在一般情況下, 每個 Sentinel 會以每 10 秒一次的頻率,向它已知的所有 主伺服器從伺服器 傳送 INFO 命令。當一個 主伺服器Sentinel 標記為 客觀下線 時,Sentinel下線主伺服器 的所有 從伺服器 傳送 INFO 命令的頻率,會從 10 秒一次改為 每秒一次

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

  1. Sentinel 和其他 Sentinel 協商 主節點 的狀態,如果 主節點 處於 SDOWN 狀態,則投票自動選出新的 主節點。將剩餘的 從節點 指向 新的主節點 進行 資料複製

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

  1. 當沒有足夠數量的 Sentinel 同意 主伺服器 下線時, 主伺服器客觀下線狀態 就會被移除。當 主伺服器 重新向 SentinelPING 命令返回 有效回覆 時,主伺服器主觀下線狀態 就會被移除。

深入剖析Redis系列(二) - Redis哨兵模式與高可用叢集

注意:一個有效的 PING 回覆可以是:+PONG-LOADING 或者 -MASTERDOWN。如果 伺服器 返回除以上三種回覆之外的其他回覆,又或者在 指定時間 內沒有回覆 PING 命令, 那麼 Sentinel 認為伺服器返回的回覆 無效non-valid)。

5. Redis Sentinel搭建

5.1. Redis Sentinel的部署須知

  1. 一個穩健的 Redis Sentinel 叢集,應該使用至少 三個 Sentinel 例項,並且保證講這些例項放到 不同的機器 上,甚至不同的 物理區域

  2. Sentinel 無法保證 強一致性

  3. 常見的 客戶端應用庫 都支援 Sentinel

  4. Sentinel 需要通過不斷的 測試觀察,才能保證高可用。

5.2. Redis Sentinel的配置檔案

# 哨兵sentinel例項執行的埠,預設26379  
port 26379
# 哨兵sentinel的工作目錄
dir ./

# 哨兵sentinel監控的redis主節點的 
## ip:主機ip地址
## port:哨兵埠號
## master-name:可以自己命名的主節點名字(只能由字母A-z、數字0-9 、這三個字元".-_"組成。)
## quorum:當這些quorum個數sentinel哨兵認為master主節點失聯 那麼這時 客觀上認為主節點失聯了  
# sentinel monitor <master-name> <ip> <redis-port> <quorum>  
sentinel monitor mymaster 127.0.0.1 6379 2

# 當在Redis例項中開啟了requirepass <foobared>,所有連線Redis例項的客戶端都要提供密碼。
# sentinel auth-pass <master-name> <password>  
sentinel auth-pass mymaster 123456  

# 指定主節點應答哨兵sentinel的最大時間間隔,超過這個時間,哨兵主觀上認為主節點下線,預設30秒  
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000  

# 指定了在發生failover主備切換時,最多可以有多少個slave同時對新的master進行同步。這個數字越小,完成failover所需的時間就越長;反之,但是如果這個數字越大,就意味著越多的slave因為replication而不可用。可以通過將這個值設為1,來保證每次只有一個slave,處於不能處理命令請求的狀態。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1  

# 故障轉移的超時時間failover-timeout,預設三分鐘,可以用在以下這些方面:
## 1. 同一個sentinel對同一個master兩次failover之間的間隔時間。  
## 2. 當一個slave從一個錯誤的master那裡同步資料時開始,直到slave被糾正為從正確的master那裡同步資料時結束。  
## 3. 當想要取消一個正在進行的failover時所需要的時間。
## 4.當進行failover時,配置所有slaves指向新的master所需的最大時間。不過,即使過了這個超時,slaves依然會被正確配置為指向master,但是就不按parallel-syncs所配置的規則來同步資料了
# sentinel failover-timeout <master-name> <milliseconds>  
sentinel failover-timeout mymaster 180000

# 當sentinel有任何警告級別的事件發生時(比如說redis例項的主觀失效和客觀失效等等),將會去呼叫這個指令碼。一個指令碼的最大執行時間為60s,如果超過這個時間,指令碼將會被一個SIGKILL訊號終止,之後重新執行。
# 對於指令碼的執行結果有以下規則:  
## 1. 若指令碼執行後返回1,那麼該指令碼稍後將會被再次執行,重複次數目前預設為10。
## 2. 若指令碼執行後返回2,或者比2更高的一個返回值,指令碼將不會重複執行。  
## 3. 如果指令碼在執行過程中由於收到系統中斷訊號被終止了,則同返回值為1時的行為相同。
# sentinel notification-script <master-name> <script-path>  
sentinel notification-script mymaster /var/redis/notify.sh

# 這個指令碼應該是通用的,能被多次呼叫,不是針對性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
複製程式碼

5.3. Redis Sentinel的節點規劃

角色 IP地址 埠號
Redis Master 10.206.20.231 16379
Redis Slave1 10.206.20.231 26379
Redis Slave2 10.206.20.231 36379
Redis Sentinel1 10.206.20.231 16380
Redis Sentinel2 10.206.20.231 26380
Redis Sentinel3 10.206.20.231 36380

5.4. Redis Sentinel的配置搭建

5.4.1. Redis-Server的配置管理

分別拷貝三份 redis.conf 檔案到 /usr/local/redis-sentinel 目錄下面。三個配置檔案分別對應 masterslave1slave2 三個 Redis 節點的 啟動配置

$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-16379.conf
$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-26379.conf
$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-36379.conf
複製程式碼

分別修改三份配置檔案如下:

  • 主節點:redis-16379.conf
daemonize yes
pidfile /var/run/redis-16379.pid
logfile /var/log/redis/redis-16379.log
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-16379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
複製程式碼
  • 從節點1:redis-26379.conf
daemonize yes
pidfile /var/run/redis-26379.pid
logfile /var/log/redis/redis-26379.log
port 26379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-26379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
slaveof 127.0.0.1 16379
複製程式碼
  • 從節點2:redis-36379.conf
daemonize yes
pidfile /var/run/redis-36379.pid
logfile /var/log/redis/redis-36379.log
port 36379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-36379.db
dir ./redis-workdir
masterauth 123456
requirepass 123456
slaveof 127.0.0.1 16379
複製程式碼

如果要做 自動故障轉移,建議所有的 redis.conf 都設定 masterauth。因為 自動故障 只會重寫 主從關係,即 slaveof,不會自動寫入 masterauth。如果 Redis 原本沒有設定密碼,則可以忽略。

5.4.2. Redis-Server啟動驗證

按順序分別啟動 163792637936379 三個 Redis 節點,啟動命令和啟動日誌如下:

Redis 的啟動命令:

$ sudo redis-server /usr/local/redis-sentinel/redis-16379.conf
$ sudo redis-server /usr/local/redis-sentinel/redis-26379.conf
$ sudo redis-server /usr/local/redis-sentinel/redis-36379.conf
複製程式碼

檢視 Redis 的啟動程式:

$ ps -ef | grep redis-server
    0  7127     1   0  2:16下午 ??         0:01.84 redis-server 0.0.0.0:16379 
    0  7133     1   0  2:16下午 ??         0:01.73 redis-server 0.0.0.0:26379 
    0  7137     1   0  2:16下午 ??         0:01.70 redis-server 0.0.0.0:36379 
複製程式碼

檢視 Redis 的啟動日誌:

  • 節點 redis-16379
$ cat /var/log/redis/redis-16379.log 
7126:C 22 Aug 14:16:38.907 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7126:C 22 Aug 14:16:38.908 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7126, just started
7126:C 22 Aug 14:16:38.908 # Configuration loaded
7127:M 22 Aug 14:16:38.910 * Increased maximum number of open files to 10032 (it was originally set to 256).
7127:M 22 Aug 14:16:38.912 * Running mode=standalone, port=16379.
7127:M 22 Aug 14:16:38.913 # Server initialized
7127:M 22 Aug 14:16:38.913 * Ready to accept connections
7127:M 22 Aug 14:16:48.416 * Slave 127.0.0.1:26379 asks for synchronization
7127:M 22 Aug 14:16:48.416 * Full resync requested by slave 127.0.0.1:26379
7127:M 22 Aug 14:16:48.416 * Starting BGSAVE for SYNC with target: disk
7127:M 22 Aug 14:16:48.416 * Background saving started by pid 7134
7134:C 22 Aug 14:16:48.433 * DB saved on disk
7127:M 22 Aug 14:16:48.487 * Background saving terminated with success
7127:M 22 Aug 14:16:48.494 * Synchronization with slave 127.0.0.1:26379 succeeded
7127:M 22 Aug 14:16:51.848 * Slave 127.0.0.1:36379 asks for synchronization
7127:M 22 Aug 14:16:51.849 * Full resync requested by slave 127.0.0.1:36379
7127:M 22 Aug 14:16:51.849 * Starting BGSAVE for SYNC with target: disk
7127:M 22 Aug 14:16:51.850 * Background saving started by pid 7138
7138:C 22 Aug 14:16:51.862 * DB saved on disk
7127:M 22 Aug 14:16:51.919 * Background saving terminated with success
7127:M 22 Aug 14:16:51.923 * Synchronization with slave 127.0.0.1:36379 succeeded
複製程式碼

以下兩行日誌日誌表明,redis-16379 作為 Redis主節點redis-26379redis-36379 作為 從節點,從 主節點 同步資料。

7127:M 22 Aug 14:16:48.416 * Slave 127.0.0.1:26379 asks for synchronization
7127:M 22 Aug 14:16:51.848 * Slave 127.0.0.1:36379 asks for synchronization
複製程式碼
  • 節點 redis-26379
$ cat /var/log/redis/redis-26379.log 
7132:C 22 Aug 14:16:48.407 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7132:C 22 Aug 14:16:48.408 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7132, just started
7132:C 22 Aug 14:16:48.408 # Configuration loaded
7133:S 22 Aug 14:16:48.410 * Increased maximum number of open files to 10032 (it was originally set to 256).
7133:S 22 Aug 14:16:48.412 * Running mode=standalone, port=26379.
7133:S 22 Aug 14:16:48.413 # Server initialized
7133:S 22 Aug 14:16:48.413 * Ready to accept connections
7133:S 22 Aug 14:16:48.413 * Connecting to MASTER 127.0.0.1:16379
7133:S 22 Aug 14:16:48.413 * MASTER <-> SLAVE sync started
7133:S 22 Aug 14:16:48.414 * Non blocking connect for SYNC fired the event.
7133:S 22 Aug 14:16:48.414 * Master replied to PING, replication can continue...
7133:S 22 Aug 14:16:48.415 * Partial resynchronization not possible (no cached master)
7133:S 22 Aug 14:16:48.417 * Full resync from master: 211d3b4eceaa3af4fe5c77d22adf06e1218e0e7b:0
7133:S 22 Aug 14:16:48.494 * MASTER <-> SLAVE sync: receiving 176 bytes from master
7133:S 22 Aug 14:16:48.495 * MASTER <-> SLAVE sync: Flushing old data
7133:S 22 Aug 14:16:48.496 * MASTER <-> SLAVE sync: Loading DB in memory
7133:S 22 Aug 14:16:48.498 * MASTER <-> SLAVE sync: Finished with success
複製程式碼
  • 節點 redis-36379
$ cat /var/log/redis/redis-36379.log 
7136:C 22 Aug 14:16:51.839 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7136:C 22 Aug 14:16:51.840 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7136, just started
7136:C 22 Aug 14:16:51.841 # Configuration loaded
7137:S 22 Aug 14:16:51.843 * Increased maximum number of open files to 10032 (it was originally set to 256).
7137:S 22 Aug 14:16:51.845 * Running mode=standalone, port=36379.
7137:S 22 Aug 14:16:51.845 # Server initialized
7137:S 22 Aug 14:16:51.846 * Ready to accept connections
7137:S 22 Aug 14:16:51.846 * Connecting to MASTER 127.0.0.1:16379
7137:S 22 Aug 14:16:51.847 * MASTER <-> SLAVE sync started
7137:S 22 Aug 14:16:51.847 * Non blocking connect for SYNC fired the event.
7137:S 22 Aug 14:16:51.847 * Master replied to PING, replication can continue...
7137:S 22 Aug 14:16:51.848 * Partial resynchronization not possible (no cached master)
7137:S 22 Aug 14:16:51.850 * Full resync from master: 211d3b4eceaa3af4fe5c77d22adf06e1218e0e7b:14
7137:S 22 Aug 14:16:51.923 * MASTER <-> SLAVE sync: receiving 176 bytes from master
7137:S 22 Aug 14:16:51.923 * MASTER <-> SLAVE sync: Flushing old data
7137:S 22 Aug 14:16:51.924 * MASTER <-> SLAVE sync: Loading DB in memory
7137:S 22 Aug 14:16:51.927 * MASTER <-> SLAVE sync: Finished with success
複製程式碼

5.4.3. Sentinel的配置管理

分別拷貝三份 redis-sentinel.conf 檔案到 /usr/local/redis-sentinel 目錄下面。三個配置檔案分別對應 masterslave1slave2 三個 Redis 節點的 哨兵配置

$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-16380.conf
$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-26380.conf
$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-36380.conf
複製程式碼
  • 節點1:sentinel-16380.conf
protected-mode no
bind 0.0.0.0
port 16380
daemonize yes
sentinel monitor master 127.0.0.1 16379 2
sentinel down-after-milliseconds master 5000
sentinel failover-timeout master 180000
sentinel parallel-syncs master 1
sentinel auth-pass master 123456
logfile /var/log/redis/sentinel-16380.log
複製程式碼
  • 節點2:sentinel-26380.conf
protected-mode no
bind 0.0.0.0
port 26380
daemonize yes
sentinel monitor master 127.0.0.1 16379 2
sentinel down-after-milliseconds master 5000
sentinel failover-timeout master 180000
sentinel parallel-syncs master 1
sentinel auth-pass master 123456
logfile /var/log/redis/sentinel-26380.log
複製程式碼
  • 節點3:sentinel-36380.conf
protected-mode no
bind 0.0.0.0
port 36380
daemonize yes
sentinel monitor master 127.0.0.1 16379 2
sentinel down-after-milliseconds master 5000
sentinel failover-timeout master 180000
sentinel parallel-syncs master 1
sentinel auth-pass master 123456
logfile /var/log/redis/sentinel-36380.log
複製程式碼

5.4.4. Sentinel啟動驗證

按順序分別啟動 163802638036380 三個 Sentinel 節點,啟動命令和啟動日誌如下:

$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-16380.conf
$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-26380.conf
$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-36380.conf
複製程式碼

檢視 Sentinel 的啟動程式:

$ ps -ef | grep redis-sentinel
    0  7954     1   0  3:30下午 ??         0:00.05 redis-sentinel 0.0.0.0:16380 [sentinel] 
    0  7957     1   0  3:30下午 ??         0:00.05 redis-sentinel 0.0.0.0:26380 [sentinel] 
    0  7960     1   0  3:30下午 ??         0:00.04 redis-sentinel 0.0.0.0:36380 [sentinel] 
複製程式碼

檢視 Sentinel 的啟動日誌:

  • 節點 sentinel-16380
$ cat /var/log/redis/sentinel-16380.log 
7953:X 22 Aug 15:30:27.245 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7953:X 22 Aug 15:30:27.245 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7953, just started
7953:X 22 Aug 15:30:27.245 # Configuration loaded
7954:X 22 Aug 15:30:27.247 * Increased maximum number of open files to 10032 (it was originally set to 256).
7954:X 22 Aug 15:30:27.249 * Running mode=sentinel, port=16380.
7954:X 22 Aug 15:30:27.250 # Sentinel ID is 69d05b86a82102a8919231fd3c2d1f21ce86e000
7954:X 22 Aug 15:30:27.250 # +monitor master master 127.0.0.1 16379 quorum 2
7954:X 22 Aug 15:30:32.286 # +sdown sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
7954:X 22 Aug 15:30:34.588 # -sdown sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
複製程式碼

sentinel-16380 節點的 Sentinel ID69d05b86a82102a8919231fd3c2d1f21ce86e000,並通過 Sentinel ID 把自身加入 sentinel 叢集中。

  • 節點 sentinel-26380
$ cat /var/log/redis/sentinel-26380.log 
7956:X 22 Aug 15:30:30.900 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7956:X 22 Aug 15:30:30.901 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7956, just started
7956:X 22 Aug 15:30:30.901 # Configuration loaded
7957:X 22 Aug 15:30:30.904 * Increased maximum number of open files to 10032 (it was originally set to 256).
7957:X 22 Aug 15:30:30.905 * Running mode=sentinel, port=26380.
7957:X 22 Aug 15:30:30.906 # Sentinel ID is 21e30244cda6a3d3f55200bcd904d0877574e506
7957:X 22 Aug 15:30:30.906 # +monitor master master 127.0.0.1 16379 quorum 2
7957:X 22 Aug 15:30:30.907 * +slave slave 127.0.0.1:26379 127.0.0.1 26379 @ master 127.0.0.1 16379
7957:X 22 Aug 15:30:30.911 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 16379
7957:X 22 Aug 15:30:36.311 * +sentinel sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
複製程式碼

sentinel-26380 節點的 Sentinel ID21e30244cda6a3d3f55200bcd904d0877574e506,並通過 Sentinel ID 把自身加入 sentinel 叢集中。此時 sentinel 叢集中已有 sentinel-16380sentinel-26380 兩個節點。

  • 節點 sentinel-36380
$ cat /var/log/redis/sentinel-36380.log 
7959:X 22 Aug 15:30:34.273 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
7959:X 22 Aug 15:30:34.274 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7959, just started
7959:X 22 Aug 15:30:34.274 # Configuration loaded
7960:X 22 Aug 15:30:34.276 * Increased maximum number of open files to 10032 (it was originally set to 256).
7960:X 22 Aug 15:30:34.277 * Running mode=sentinel, port=36380.
7960:X 22 Aug 15:30:34.278 # Sentinel ID is fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
7960:X 22 Aug 15:30:34.278 # +monitor master master 127.0.0.1 16379 quorum 2
7960:X 22 Aug 15:30:34.279 * +slave slave 127.0.0.1:26379 127.0.0.1 26379 @ master 127.0.0.1 16379
7960:X 22 Aug 15:30:34.283 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 16379
7960:X 22 Aug 15:30:34.993 * +sentinel sentinel 21e30244cda6a3d3f55200bcd904d0877574e506 127.0.0.1 26380 @ master 127.0.0.1 16379
複製程式碼

sentinel-36380 節點的 Sentinel IDfd166dc66425dc1d9e2670e1f17cb94fe05f5fc7,並通過 Sentinel ID 把自身加入 sentinel 叢集中。此時 sentinel 叢集中已有 sentinel-16380sentinel-26380sentinel-36380 三個節點。

5.4.5. Sentinel配置重新整理

  • 節點1:sentinel-16380.conf

sentinel-16380.conf 檔案新生成如下的配置項:

# Generated by CONFIG REWRITE
dir "/usr/local/redis-sentinel"
sentinel config-epoch master 0
sentinel leader-epoch master 0
sentinel known-slave master 127.0.0.1 36379
sentinel known-slave master 127.0.0.1 26379
sentinel known-sentinel master 127.0.0.1 26380 21e30244cda6a3d3f55200bcd904d0877574e506
sentinel known-sentinel master 127.0.0.1 36380 fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
sentinel current-epoch 0
複製程式碼

可以注意到,sentinel-16380.conf 重新整理寫入了 Redis 主節點關聯的所有 從節點 redis-26379redis-36379,同時寫入了其餘兩個 Sentinel 節點 sentinel-26380sentinel-36380IP 地址,埠號Sentinel ID

# Generated by CONFIG REWRITE
dir "/usr/local/redis-sentinel"
sentinel config-epoch master 0
sentinel leader-epoch master 0
sentinel known-slave master 127.0.0.1 26379
sentinel known-slave master 127.0.0.1 36379
sentinel known-sentinel master 127.0.0.1 36380 fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7
sentinel known-sentinel master 127.0.0.1 16380 69d05b86a82102a8919231fd3c2d1f21ce86e000
sentinel current-epoch 0
複製程式碼

可以注意到,sentinel-26380.conf 重新整理寫入了 Redis 主節點關聯的所有 從節點 redis-26379redis-36379,同時寫入了其餘兩個 Sentinel 節點 sentinel-36380sentinel-16380IP 地址,埠號Sentinel ID

# Generated by CONFIG REWRITE
dir "/usr/local/redis-sentinel"
sentinel config-epoch master 0
sentinel leader-epoch master 0
sentinel known-slave master 127.0.0.1 36379
sentinel known-slave master 127.0.0.1 26379
sentinel known-sentinel master 127.0.0.1 16380 69d05b86a82102a8919231fd3c2d1f21ce86e000
sentinel known-sentinel master 127.0.0.1 26380 21e30244cda6a3d3f55200bcd904d0877574e506
sentinel current-epoch 0
複製程式碼

可以注意到,sentinel-36380.conf 重新整理寫入了 Redis 主節點關聯的所有 從節點 redis-26379redis-36379,同時寫入了其餘兩個 Sentinel 節點 sentinel-16380sentinel-26380IP 地址,埠號Sentinel ID

5.5. Sentinel時客戶端命令

  • 檢查其他 Sentinel 節點的狀態,返回 PONG 為正常。
> PING sentinel
複製程式碼
  • 顯示被監控的所有 主節點 以及它們的狀態。
> SENTINEL masters
複製程式碼
  • 顯示指定 主節點 的資訊和狀態。
> SENTINEL master <master_name>
複製程式碼
  • 顯示指定 主節點 的所有 從節點 以及它們的狀態。
> SENTINEL slaves <master_name>
複製程式碼

返回指定 主節點IP 地址和 。如果正在進行 failover 或者 failover 已經完成,將會顯示被提升為 主節點從節點IP 地址和

> SENTINEL get-master-addr-by-name <master_name>
複製程式碼
  • 重置名字匹配該 正規表示式 的所有的 主節點 的狀態資訊,清除它之前的 狀態資訊,以及 從節點 的資訊。
> SENTINEL reset <pattern>
複製程式碼
  • 強制當前 Sentinel 節點執行 failover,並且不需要得到其他 Sentinel 節點的同意。但是 failover 後會將 最新的配置 傳送給其他 Sentinel 節點。
SENTINEL failover <master_name>
複製程式碼

6. Redis Sentinel故障切換與恢復

6.1. Redis CLI客戶端跟蹤

上面的日誌顯示,redis-16379 節點為 主節點,它的程式 ID7127。為了模擬 Redis 主節點故障,強制殺掉這個程式。

$ kill -9 7127
複製程式碼

使用 redis-cli 客戶端命令進入 sentinel-16380 節點,檢視 Redis 節點 的狀態資訊。

$ redis-cli -p 16380
複製程式碼
  • 檢視 Redis 主從叢集的 主節點 資訊。可以發現 redis-26379 晉升為 新的主節點

127.0.0.1:16380> SENTINEL master master
 1) "name"
 2) "master"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "26379"
 7) "runid"
 8) "b8ca3b468a95d1be5efe1f50c50636cafe48c59f"
 9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "588"
19) "last-ping-reply"
20) "588"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "9913"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "663171"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "2"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "180000"
39) "parallel-syncs"
40) "1"
複製程式碼

6.2. Redis Sentinel日誌跟蹤

檢視任意 Sentinel 節點的日誌如下:

7954:X 22 Aug 18:40:22.504 # +tilt #tilt mode entered
7954:X 22 Aug 18:40:32.197 # +tilt #tilt mode entered
7954:X 22 Aug 18:41:02.241 # -tilt #tilt mode exited
7954:X 22 Aug 18:48:24.550 # +sdown master master 127.0.0.1 16379
7954:X 22 Aug 18:48:24.647 # +new-epoch 1
7954:X 22 Aug 18:48:24.651 # +vote-for-leader fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 1
7954:X 22 Aug 18:48:25.678 # +odown master master 127.0.0.1 16379 #quorum 3/2
7954:X 22 Aug 18:48:25.678 # Next failover delay: I will not start a failover before Wed Aug 22 18:54:24 2018
7954:X 22 Aug 18:48:25.709 # +config-update-from sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379
7954:X 22 Aug 18:48:25.710 # +switch-master master 127.0.0.1 16379 127.0.0.1 26379
7954:X 22 Aug 18:48:25.710 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 26379
7954:X 22 Aug 18:48:25.711 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
7954:X 22 Aug 18:48:30.738 # +sdown slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
7954:X 22 Aug 19:38:23.479 # -sdown slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
複製程式碼
  • 分析日誌,可以發現 redis-16329 節點先進入 sdown 主觀下線 狀態。
+sdown master master 127.0.0.1 16379
複製程式碼
  • 哨兵檢測到 redis-16329 出現故障,Sentinel 進入一個 新紀元,從 0 變為 1
+new-epoch 1
複製程式碼
  • 三個 Sentinel 節點開始協商 主節點 的狀態,判斷其是否需要 客觀下線
+vote-for-leader fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 1
複製程式碼
  • 超過 quorum 個數的 Sentinel 節點認為 主節點 出現故障,redis-16329 節點進入 客觀下線 狀態。
+odown master master 127.0.0.1 16379 #quorum 3/2
複製程式碼
  • Sentinal 進行 自動故障切換,協商選定 redis-26329 節點作為新的 主節點
+switch-master master 127.0.0.1 16379 127.0.0.1 26379
複製程式碼
  • redis-36329 節點和已經 客觀下線redis-16329 節點成為 redis-26479從節點
7954:X 22 Aug 18:48:25.710 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 26379
7954:X 22 Aug 18:48:25.711 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379
複製程式碼

6.3. Redis的配置檔案

分別檢視三個 redis 節點的配置檔案,發生 主從切換redis.conf 的配置會自動發生重新整理。

  • 節點 redis-16379
daemonize yes
pidfile "/var/run/redis-16379.pid"
logfile "/var/log/redis/redis-16379.log"
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-16379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
複製程式碼
  • 節點 redis-26379
daemonize yes
pidfile "/var/run/redis-26379.pid"
logfile "/var/log/redis/redis-26379.log"
port 26379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-26379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
複製程式碼
  • 節點 redis-36379
daemonize yes
pidfile "/var/run/redis-36379.pid"
logfile "/var/log/redis/redis-36379.log"
port 36379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-36379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
slaveof 127.0.0.1 26379
複製程式碼

分析redis-26379 節點 slaveof 配置被移除,晉升為 主節點redis-16379 節點處於 當機狀態redis-36379slaveof 配置更新為 127.0.0.1 redis-26379,成為 redis-26379從節點

重啟節點 redis-16379。待正常啟動後,再次檢視它的 redis.conf 檔案,配置如下:

daemonize yes
pidfile "/var/run/redis-16379.pid"
logfile "/var/log/redis/redis-16379.log"
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename "dump-16379.db"
dir "/usr/local/redis-sentinel/redis-workdir"
masterauth "123456"
requirepass "123456"
# Generated by CONFIG REWRITE
slaveof 127.0.0.1 26379
複製程式碼

節點 redis-16379 的配置檔案新增一行 slaveof 配置屬性,指向 redis-26379,即成為 新的主節點從節點

小結

本文首先對 Redis 實現高可用的幾種模式做出了闡述,指出了 Redis 主從複製 的不足之處,進一步引入了 Redis Sentinel 哨兵模式 的相關概念,深入說明了 Redis Sentinel具體功能基本原理高可用搭建自動故障切換 驗證等。

當然,Redis Sentinel 僅僅解決了 高可用 的問題,對於 主節點 單點寫入和單節點無法擴容等問題,還需要引入 Redis Cluster 叢集模式 予以解決。

參考

《Redis 開發與運維》


歡迎關注技術公眾號: 零壹技術棧

零壹技術棧

本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。

相關文章