1. 程式人生 > >redis哨兵架構的基礎知識及部署和管理

redis哨兵架構的基礎知識及部署和管理

一、前言

1、哨兵的介紹

sentinal,中文名是哨兵

哨兵是redis叢集架構中非常重要的一個元件,主要功能如下
(1)叢集監控,負責監控redis master和slave程序是否正常工作
(2)訊息通知,如果某個redis例項有故障,那麼哨兵負責傳送訊息作為報警通知給管理員
(3)故障轉移,如果master node掛掉了,會自動轉移到slave node上
(4)配置中心,如果故障轉移發生了,通知client客戶端新的master地址

哨兵本身也是分散式的,作為一個哨兵叢集去執行,互相協同工作
(1)故障轉移時,判斷一個master node是宕機了,需要大部分的哨兵都同意才行,涉及到了分散式選舉的問題
(
2)即使部分哨兵節點掛掉了,哨兵叢集還是能正常工作的,因為如果一個作為高可用機制重要組成部分的故障轉移系統本身是單點的,那就很坑爹了 目前採用的是sentinal 2版本,sentinal 2相對於sentinal 1來說,重寫了很多程式碼,主要是讓故障轉移的機制和演算法變得更加健壯和簡單

2、哨兵的核心知識

1)哨兵至少需要3個例項,來保證自己的健壯性
(2)哨兵 + redis主從的部署架構,是不會保證資料零丟失的,只能保證redis叢集的高可用性
(3)對於哨兵 + redis主從這種複雜的部署架構,儘量在測試環境和生產環境,都進行充足的測試和演練

3、為什麼redis哨兵叢集只有2個節點無法正常工作?

哨兵叢集必須部署2個以上節點
如果哨兵叢集僅僅部署了個2個哨兵例項,quorum=1

+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

Configuration: quorum = 1

master宕機,s1和s2中只要有1個哨兵認為master宕機就可以還行切換,同時s1和s2中會選舉出一個哨兵來執行故障轉移
同時這個時候,需要majority,也就是大多數哨兵都是執行的,2個哨兵的majority就是2(2的majority=2
,3的majority=2,5的majority=3,4的majority=2),2個哨兵都執行著,就可以允許執行故障轉移 但是如果整個M1和S1執行的機器宕機了,那麼哨兵只有1個了,此時就沒有majority來允許執行故障轉移,雖然另外一臺機器還有一個R1,但是故障轉移不會執行

4、經典的3節點哨兵叢集

       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

Configuration: quorum = 2,majority

如果M1所在機器宕機了,那麼三個哨兵還剩下2個,S2和S3可以一致認為master宕機,然後選舉出一個來執行故障轉移
同時3個哨兵的majority是2,所以還剩下的2個哨兵執行著,就可以允許執行故障轉移

 二、redis哨兵主備切換的資料丟失問題及解決方法

1、兩種資料丟失的情況

主備切換的過程,可能會導致資料丟失

(1)非同步複製導致的資料丟失
因為master -> slave的複製是非同步的,所以可能有部分資料還沒複製到slave,master就宕機了,此時這些部分資料就丟失了

(2)腦裂導致的資料丟失
腦裂,也就是說,某個master所在機器突然脫離了正常的網路,跟其他slave機器不能連線,但是實際上master還執行著
此時哨兵可能就會認為master宕機了,然後開啟選舉,將其他slave切換成了master
這個時候,叢集裡就會有兩個master,也就是所謂的腦裂
此時雖然某個slave被切換成了master,但是可能client還沒來得及切換到新的master,還繼續寫向舊master的資料可能也丟失了
因此舊master再次恢復的時候,會被作為一個slave掛到新的master上去,自己的資料會清空,重新從新的master複製資料

2、解決非同步複製和腦裂導致的資料丟失

min-slaves-to-write 1
min-slaves-max-lag 10

要求至少有1個slave,資料複製和同步的延遲不能超過10秒
如果說一旦所有的slave,資料複製和同步的延遲都超過了10秒鐘,那麼這個時候,master就不會再接收任何請求了
上面兩個配置可以減少非同步複製和腦裂導致的資料丟失

(1)減少非同步複製的資料丟失
有了min-slaves-max-lag這個配置,就可以確保說,一旦slave複製資料和ack延時太長,就認為可能master宕機後損失的資料太多了,那麼就拒絕寫請求,這樣可以把master宕機時由於部分資料未同步到slave導致的資料丟失降低的可控範圍內

(2)減少腦裂的資料丟失
如果一個master出現了腦裂,跟其他slave丟了連線,那麼上面兩個配置可以確保說,如果不能繼續給指定數量的slave傳送資料,而且slave超過10秒沒有給自己ack訊息,那麼就直接拒絕客戶端的寫請求
這樣腦裂後的舊master就不會接受client的新資料,也就避免了資料丟失
上面的配置就確保了,如果跟任何一個slave丟了連線,在10秒後發現沒有slave給自己ack,那麼就拒絕新的寫請求
因此在腦裂場景下,最多就丟失10秒的資料

 三、redis哨兵的核心底層原理

1、sdown和odown轉換機制

sdown和odown兩種失敗狀態

sdown是主觀宕機,就一個哨兵如果自己覺得一個master宕機了,那麼就是主觀宕機

odown是客觀宕機,如果quorum數量的哨兵都覺得一個master宕機了,那麼就是客觀宕機

sdown達成的條件很簡單,如果一個哨兵ping一個master,超過了is-master-down-after-milliseconds指定的毫秒數之後,就主觀認為master宕機

sdown到odown轉換的條件很簡單,如果一個哨兵在指定時間內,收到了quorum指定數量的其他哨兵也認為那個master是sdown了,那麼就認為是odown了,客觀認為master宕

2、哨兵叢集的自動發現機制

哨兵互相之間的發現,是通過redis的pub/sub系統實現的,每個哨兵都會往__sentinel__:hello這個channel裡傳送一個訊息,這時候所有其他哨兵都可以消費到這個訊息,並感知到其他的哨兵的存在

每隔兩秒鐘,每個哨兵都會往自己監控的某個master+slaves對應的__sentinel__:hello channel裡傳送一個訊息,內容是自己的host、ip和runid還有對這個master的監控配置

每個哨兵也會去監聽自己監控的每個master+slaves對應的__sentinel__:hello channel,然後去感知到同樣在監聽這個master+slaves的其他哨兵的存在

每個哨兵還會跟其他哨兵交換對master的監控配置,互相進行監控配置的同步

3、slave配置的自動糾正

哨兵會負責自動糾正slave的一些配置,比如slave如果要成為潛在的master候選人,哨兵會確保slave在複製現有master的資料; 如果slave連線到了一個錯誤的master上,比如故障轉移之後,那麼哨兵會確保它們連線到正確的master上

4、slave->master選舉演算法

如果一個master被認為odown了,而且majority哨兵都允許了主備切換,那麼某個哨兵就會執行主備切換操作,此時首先要選舉一個slave來
會考慮slave的一些資訊
(1)跟master斷開連線的時長
(2)slave優先順序
(3)複製offset
(4)run id

如果一個slave跟master斷開連線已經超過了down-after-milliseconds的10倍,外加master宕機的時長,那麼slave就被認為不適合選舉為master
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

接下來會對slave進行排序

(1)按照slave優先順序進行排序,slave priority越低,優先順序就越高
(2)如果slave priority相同,那麼看replica offset,哪個slave複製了越多的資料,offset越靠後,優先順序就越高
(3)如果上面兩個條件都相同,那麼選擇一個run id比較小的那個slave

5、quorum和majority

每次一個哨兵要做主備切換,首先需要quorum數量的哨兵認為odown,然後選舉出一個哨兵來做切換,這個哨兵還得得到majority哨兵的授權,才能正式執行切換

如果quorum < majority,比如5個哨兵,majority就是3,quorum設定為2,那麼就3個哨兵授權就可以執行切換

但是如果quorum >= majority,那麼必須quorum數量的哨兵都授權,比如5個哨兵,quorum是5,那麼必須5個哨兵都同意授權,才能執行切換

6、configuration epoch

哨兵會對一套redis master+slave進行監控,有相應的監控的配置

執行切換的那個哨兵,會從要切換到的新master(salve->master)那裡得到一個configuration epoch,這就是一個version號,每次切換的version號都必須是唯一的

如果第一個選舉出的哨兵切換失敗了,那麼其他哨兵,會等待failover-timeout時間,然後接替繼續執行切換,此時會重新獲取一個新的configuration epoch,作為新的version號

7、configuraiton傳播

哨兵完成切換之後,會在自己本地更新生成最新的master配置,然後同步給其他的哨兵,就是通過之前說的pub/sub訊息機制

這裡之前的version號就很重要了,因為各種訊息都是通過一個channel去釋出和監聽的,所以一個哨兵完成一次新的切換之後,新的master配置是跟著新的version號的

其他的哨兵都是根據版本號的大小來更新自己的master配置的

 四、部署sentinel(三節點)

OS:centos7

redis:4.0.12

1、安裝redis並配置主從

參考https://www.cnblogs.com/panwenbin-logs/p/10242027.html

https://www.cnblogs.com/panwenbin-logs/p/10257741.html

2、配置sentinel(所有節點,注意bind地址)

[[email protected] ~]# cd /usr/local/redis   #redis解壓目錄
[[email protected]-master redis]# cp sentinel.conf sentinel.conf-bak
[[email protected]-master redis]# mkdir /etc/sentinal
[[email protected]-master redis]# mkdir -p /var/sentinal/26379
[[email protected]-master redis]# cp sentinel.conf /etc/sentinal/
[[email protected]-master redis]# cd /etc/sentinal/
[[email protected]-master sentinal]# cat sentinel.conf 
port 26379
bind 192.168.1.132    #本機地址
dir /var/sentinal/26379 
sentinel monitor mymaster 192.168.1.132 6379 2  #mymaster是叢集的名稱可自定義,IP地為叢集中master的地址,注意與bind的區別 6379表示埠 2表示 需要多少哨兵同意才能執行故障轉移操作
sentinel down-after-milliseconds mymaster 30000  #超過多少毫秒跟一個redis例項斷了連線,哨兵就可能認為這個redis例項掛了
sentinel failover-timeout mymaster 60000  #failover轉移時間,超出此時間認為master轉移失效,重新開始轉移
sentinel parallel-syncs mymaster 1 #新的master別切換之後,同時有多少個slave被切換到去連線新master,重新做同步,數字越低,花費的時間越多
protected-mode no #關閉安全模式,否則會報錯
sentinel auth
-pass mymaster redis-pass #如果叢集設定了密碼,需要新增
daemonize yes #後臺程序
logfile /var/log/sentinal/sentinal.log #日誌路徑

啟動

[[email protected] sentinal]# redis-sentinel /etc/sentinal/sentinel.conf   #測試,生產應該將命令放入後臺執行
22120:X 13 Jan 14:33:30.411 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
22120:X 13 Jan 14:33:30.411 # Redis version=4.0.12, bits=64, commit=00000000, modified=0, pid=22120, just started
22120:X 13 Jan 14:33:30.411 # Configuration loaded
22120:X 13 Jan 14:33:30.413 * Increased maximum number of open files to 10032 (it was originally set to 1024).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 4.0.12 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 22120
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

22120:X 13 Jan 14:33:30.414 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
22120:X 13 Jan 14:33:30.414 # Sentinel ID is 704dddbb7700554d21df80d7be55830b2d4be0a7
22120:X 13 Jan 14:33:30.414 # +monitor master mymaster 192.168.1.132 6379 quorum 2
22120:X 13 Jan 14:33:30.416 * +slave slave 192.168.1.133:6379 192.168.1.133 6379 @ mymaster 192.168.1.132 6379 
22120:X 13 Jan 14:33:37.775 * +sentinel sentinel d0864c9cddd5d9dd9165f8ac1c493a226189f093 192.168.1.133 26379 @ mymaster 192.168.1.132 6379
22120:X 13 Jan 14:33:42.751 * +sentinel sentinel 8ab3bf99d413a3810b7e0976fad94f88853b2445 192.168.1.134 26379 @ mymaster 192.168.1.132 6379

檢查

[[email protected] 26379]# redis-cli -h 192.168.1.132 -p 26379
192.168.1.132:26379>  sentinel master mymaster #檢視master的資訊
192.168.1.132:26379> SENTINEL slaves mymaster  #檢視slave的資訊
192.168.1.132:26379> SENTINEL sentinels mymaster  #檢視sentinel的資訊,本機sentinel資訊不顯示
192.168.1.132:26379> SENTINEL get-master-addr-by-name mymaster #檢視redis叢集master的IP地址和埠

 五、哨兵管理

1、哨兵節點的增加和刪除

1.增加sentinal,會自動發現

2.刪除sentinal的步驟
(1)停止sentinal程序
(2)SENTINEL RESET *,在所有sentinal上執行,清理所有的master狀態
(3)SENTINEL MASTER mastername,在所有sentinal上執行,檢視所有sentinal對數量是否達成了一致

2、slave的永久下線

讓master摘除某個已經下線的slave:SENTINEL RESET mastername,在所有的哨兵上面執行

3、slave切換為Master的優先順序

slave->master選舉優先順序:slave-priority,值越小優先順序越高

4、基於哨兵叢集架構下的安全認證

每個slave都有可能切換成master,所以每個例項都要配置兩個指令

master上啟用安全認證,requirepass
master連線口令,masterauth

sentinal,sentinel auth-pass <master-group-name> <pass>

5、容災演練

通過哨兵看一下當前的master:SENTINEL get-master-addr-by-name mymaster
把master節點kill -9掉,pid檔案也刪除掉
檢視sentinal的日誌,是否出現+sdown字樣,識別出了master的宕機問題; 然後出現+odown字樣,就是指定的quorum哨兵數量,都認為master宕機了

(1)三個哨兵程序都認為master是sdown了
(2)超過quorum指定的哨兵程序都認為sdown之後,就變為odown
(3)哨兵1是被選舉為要執行後續的主備切換的那個哨兵
(4)哨兵1去新的master(slave)獲取了一個新的config version
(5)嘗試執行failover
(6)投票選舉出一個slave區切換成master,每隔哨兵都會執行一次投票
(7)讓salve,slaveof noone,不讓它去做任何節點的slave了; 把slave提拔成master; 舊的master認為不再是master了
(8)哨兵就自動認為之前的132:6379變成了slave了,133:6379變成了master了
(9)哨兵去探查了一下132:6379這個salve的狀態,認為它sdown了

所有哨兵選舉出了一個,來執行主備切換操作
如果哨兵的majority都存活著,那麼就會執行主備切換操作
再通過哨兵看一下master:SENTINEL get-master-addr-by-name mymaster
嘗試連線一下新的master
故障恢復,再將舊的master重新啟動,檢視是否被哨兵自動切換成slave節點

(1)手動殺掉master
(2)哨兵能否執行主備切換,將slave切換為master
(3)哨兵完成主備切換後,新的master能否使用
(4)故障恢復,將舊的master重新啟動
(5)哨兵能否自動將舊的master變為slave,掛接到新的master上面去,而且也是可以使用的

 

[[email protected] 26379]# ps aux|grep redis
[[email protected]-master 26379]# kill -9 22065 
檢視sentinel日誌
....
22120:X 13 Jan 15:21:35.699 # +sdown master mymaster 192.168.1.132 6379
22120:X 13 Jan 15:21:35.832 # +new-epoch 1
22120:X 13 Jan 15:21:35.834 # +vote-for-leader d0864c9cddd5d9dd9165f8ac1c493a226189f093 1
22120:X 13 Jan 15:21:36.182 # +config-update-from sentinel d0864c9cddd5d9dd9165f8ac1c493a226189f093 192.168.1.133 26379 @ mymaster 192.168.1.132 6379
22120:X 13 Jan 15:21:36.182 # +switch-master mymaster 192.168.1.132 6379 192.168.1.133 6379
22120:X 13 Jan 15:21:36.182 * +slave slave 192.168.1.132:6379 192.168.1.132 6379 @ mymaster 192.168.1.133 6379
22120:X 13 Jan 15:22:06.243 # +sdown slave 192.168.1.132:6379 192.168.1.132 6379 @ mymaster 192.168.1.133 6379
[[email protected] 26379]# systemctl start redis #啟動redis,在檢視日誌
22120:X 13 Jan 15:23:33.399 # -sdown slave 192.168.1.132:6379 192.168.1.132 6379 @ mymaster 192.168.1.133 6379
[[email protected] 26379]# redis-cli -h 192.168.1.132 -p 26379
192.168.1.132:26379> SENTINEL get-master-addr-by-name mymaster #雖然舊的master恢復上線後,但是可以看到master並沒有進行切換
1) "192.168.1.133"
2) "6379"

#redis狀態改變在sentinel的配置檔案中也會自動更改