目錄
基礎知識
功能
sentinal
,中文名是哨兵。哨兵是redis集羣架構中非常重要的一個組件,主要功能如下
集羣監控
,負責監控redis master和slave進程是否正常工作消息通知
,如果某個redis實例有故障,那麼哨兵負責發送消息作爲報警通知給管理員故障轉移
,如果master node掛掉了,會自動轉移到slave node上配置中心
,如果故障轉移發生了,通知client客戶端新的master地址
哨兵分佈式
哨兵本身也是分佈式的,作爲一個哨兵集羣去運行,互相協同工作。
- 故障轉移時,判斷一個master node是宕機了,需要大部分的哨兵都同意才行,涉及到了分佈式選舉的問題
- 即使部分哨兵節點掛掉了,哨兵集羣還是能正常工作的,因爲如果一個作爲高可用機制重要組成部分的故障轉移系統本身是單點的,那就很坑爹了
目前採用的是
sentinal 2
版本,sentinal 2相對於sentinal 1來說,重寫了很多代碼,主要是讓故障轉移的機制和算法變得更加健壯和簡單。
哨兵核心點
- 哨兵至少需要3個實例,來保證自己的健壯性
哨兵 + redis主從
的部署架構,是不會保證數據零丟失的,只能保證redis集羣的高可用性- 對於
哨兵 + redis
主從這種複雜的部署架構,儘量在測試環境和生產環境,都進行充足的測試和演練
哨兵集羣
雙節點集羣缺陷
爲什麼redis哨兵集羣只有2個節點無法正常工作?
- 哨兵集羣必須部署2個以上節點
- 如果哨兵集羣僅僅部署了個2個哨兵實例,
quorum=1
兩個實例,一個master,一個slave。
+----+ +----+
| 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,但是故障轉移不會執行
經典的3節點哨兵集羣
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
Configuration: quorum = 2,majority
- 如果M1所在機器宕機了,那麼三個哨兵還剩下2個,S2和S3可以一致認爲master宕機,然後選舉出一個來執行故障轉移
- 同時
3個哨兵的majority是2
,所以還剩下的2個哨兵運行着,就可以允許執行故障轉移
數據丟失
主備切換的過程,可能會導致數據丟失,
兩種數據丟失
的情況
異步複製
因爲master -> slave
的複製是異步
的,所以可能有部分數據還沒複製到slave,master就宕機了,此時這些部分數據就丟失了。
腦裂
- 腦裂,某個master所在機器突然脫離了正常的網絡,跟其他slave機器不能連接,但是實際上master還運行着
- 此時哨兵可能就會認爲master宕機了,然後開啓選舉,將其他slave切換成了master
- 這個時候,集羣裏就會有兩個master,也就是所謂的腦裂
- 此時雖然某個slave被切換成了master,但是可能client還沒來得及切換到新的master,還繼續寫向舊master的數據可能也丟失了
- 因此舊master再次恢復的時候,會被作爲一個slave掛到新的master上去,自己的數據會清空,重新從新的master複製數據
數據丟失解決
解決異步複製和腦裂導致的數據丟失
主要是redis.conf配置文件的兩個配置
min-slaves-to-write 1
min-slaves-max-lag 10
- 要求至少有1個slave,數據複製和同步的延遲不能超過10秒
- 如果說一旦所有的slave,數據複製和同步的延遲都超過了10秒鐘,那麼這個時候,master就不會再接收任何請求了
上面兩個配置可以減少異步複製和腦裂導致的數據丟失
減少異步複製的數據丟失
- redis: 有了
min-slaves-max-lag
這個配置,就可以確保說,一旦slave複製數據和ack延時太長
,就認爲可能master宕機後損失的數據太多了,那麼就拒絕寫請求,這樣可以把master宕機時由於部分數據未同步到slave導致的數據丟失降低的可控範圍內。 - client端: 降級限流,減少請求涌入;或者發送消息隊列。
減少腦裂的數據丟失
- 如果一個master出現了腦裂,跟其他slave丟了連接,那麼
上面兩個配置
可以確保說,如果不能繼續給指定數量的slave發送數據,而且slave超過10秒沒有給自己ack消息,那麼就直接拒絕客戶端的寫請求 - 這樣腦裂後的舊master就不會接受client的新數據,也就避免了數據丟失
- 上面的配置就確保了,如果跟任何一個slave丟了連接,在10秒後發現沒有slave給自己ack,那麼就拒絕新的寫請求
- 因此在腦裂場景下,最多就丟失10秒的數據
- 相應的client端也應做: 降級限流,減少請求涌入;或者發送消息隊列。
核心原理
sdown和odown轉換機制
sdown和odown兩種失敗狀態
sdown
是主觀宕機,就一個哨兵如果自己覺得一個master宕機了,那麼就是主觀宕機
sdown達成的條件很簡單,如果一個哨兵ping一個master,超過了
is-master-down-after-milliseconds
指定的毫秒數之後,就主觀認爲master宕機
odown
是客觀宕機,如果quorum數量
的哨兵都覺得一個master宕機了,那麼就是客觀宕機
sdown到odown轉換的條件很簡單,如果一個哨兵在指定時間內,收到了
quorum指定數量
的其他哨兵也認爲那個master是sdown了,那麼就認爲是odown了,客觀認爲master宕機
quorum和majority
-
每次一個哨兵要做主備切換,首先需要quorum數量的哨兵認爲odown,然後選舉出一個哨兵來做切換,這個哨兵還得得到majority哨兵的授權,才能正式執行切換
-
如果quorum < majority,比如5個哨兵,majority就是3,quorum設置爲2,那麼就3個哨兵授權就可以執行切換
-
但是如果quorum >= majority,那麼必須quorum數量的哨兵都授權,比如5個哨兵,quorum是5,那麼必須5個哨兵都同意授權,才能執行切換
哨兵集羣的自動發現機制
- 哨兵互相之間的發現,是通過redis的
pub/sub系統
(發佈訂閱機制)實現的,每個哨兵都會往__sentinel__:hello
這個channel裏發送一個消息,這時候所有其他哨兵都訂閱並消費
到這個消息,並感知到其他的哨兵的存在。 - 每隔兩秒鐘,每個哨兵都會往自己監控的
某個master+slaves
對應的__sentinel__:hello
channel裏發送一個消息,內容是自己的host、ip和runid
還有對這個master的監控配置。 - 每個哨兵也會去監聽自己監控的每個
master+slaves
對應的__sentinel__:hello
channel,然後去感知到同樣在監聽這個master+slaves的其他哨兵的存在。 - 每個哨兵還會跟其他哨兵交換對master的監控配置,互相進行監控配置的同步
slave配置的自動糾正
- 哨兵會負責自動糾正slave的一些配置;
- 比如slave如果要成爲潛在的master候選人,哨兵會確保slave在複製現有master的數據;
- 如果slave連接到了一個錯誤的master上,比如故障轉移之後,那麼哨兵會確保它們連接到正確的master上
slave->master選舉算法
如果一個master被認爲odown了,而且majority哨兵
都允許了主備切換,那麼某個哨兵就會執行主備切換
操作,此時首先要選舉一個slave來。
- 會考慮slave的一些信息
- 跟master斷開連接的時長
- slave優先級
- 複製offset
- run id
- 如果一個slave跟master斷開連接已經超過了
down-after-milliseconds
的10倍,外加master宕機的時長,那麼slave就被認爲不適合選舉爲master。
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
- 接下來會對slave進行排序
- 按照slave優先級進行排序,
slave priority
(redis.conf配置)越低,優先級就越高 - 如果slave priority相同,那麼看
replica offset
,哪個slave複製了越多的數據,offset越靠後,優先級就越高 - 如果上面兩個條件都相同,那麼選擇一個
run id
比較小的那個slave
configuration epoch
- 哨兵會對一套redis
master+slave
進行監控,有相應的監控的配置 - 執行主備切換的那個哨兵,會從要切換到的新master(salve->master)那裏得到一個
configuration epoch
,這就是一個version號,每次切換的version號都必須是唯一的 - 如果第一個選舉出的哨兵切換失敗了,那麼其他哨兵,會等待
failover-timeout
時間,然後接替繼續執行切換,此時會重新獲取一個新的configuration epoch,作爲新的version號
configuraiton傳播
- 哨兵完成切換之後,會在自己本地更新生成最新的master配置,然後同步給其他的哨兵,就是通過之前說的
pub/sub
消息機制 - 這裏之前的version號就很重要了,因爲各種消息都是通過一個channel去發佈和監聽的,所以一個哨兵完成一次新的切換之後,新的master配置是跟着新的version號的
- 其他的哨兵都是根據版本號的大小來更新自己的master配置的