1. 程式人生 > >Redis學習三:Redis高可用之哨兵模式

Redis學習三:Redis高可用之哨兵模式

申明

本文章首發自本人公眾號:壹枝花算不算浪漫,如若轉載請標明來源!

感興趣的小夥伴可關注個人公眾號:壹枝花算不算浪漫

22.jpg

前言

Redis 的 Sentinel 系統用於管理多個 Redis 伺服器(instance), 該系統執行以下三個任務:

  • 監控(Monitoring): Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。
  • 提醒(Notification): 當被監控的某個 Redis 伺服器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程式傳送通知。
  • 自動故障遷移(Automatic failover): 當一個主伺服器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會將失效主伺服器的其中一個從伺服器升級為新的主伺服器, 並讓失效主伺服器的其他從伺服器改為複製新的主伺服器; 當客戶端試圖連線失效的主伺服器時, 叢集也會向客戶端返回新主伺服器的地址, 使得叢集可以使用新主伺服器代替失效伺服器。

Redis Sentinel 是一個分散式系統, 你可以在一個架構中執行多個 Sentinel 程序(progress), 這些程序使用流言協議(gossip protocols)來接收關於主伺服器是否下線的資訊, 並使用投票協議(agreement protocols)來決定是否執行自動故障遷移, 以及選擇哪個從伺服器作為新的主伺服器。

什麼是高可用?

架構上,我們講高可用,一般說系統99%、99.9%、99.99% 等更多情形

例如一年365天 * 99.99% = 365.9天是可以對外提供服務的,換算下來是可以宕機
3153.6s,換成52.56min,0.876h

一般系統不可用可以分為以下幾種情況:

  1. 機器宕機了
  2. JVM程序OOM了
  3. 機器cpu 100%
  4. 磁碟滿了,系統各種IO報錯
  5. 其他

例如Redis,我們使用主從架構,是單個master多個slave的,如果master宕機了該如何處理?此時Redis就無法寫入了,如果我們使用哨兵模式,是會自動將某個slave提升為master的,繼續對外提供服務。

哨兵模式

sentinal:哨兵,它是redis叢集中非常重要的一個元件,主要功能如下:

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

哨兵本身也是分散式的,作為一個哨兵叢集去執行,互相協同工作

  1. 故障轉移時,判斷一個master node是宕機了,需要大部分哨兵都同意才行,涉及了分散式選舉問題
  2. 即使部分哨兵節點掛掉了,哨兵叢集還是能正常工作的

哨兵配置

Redis 原始碼中包含了一個名為 sentinel.conf 的檔案, 這個檔案是一個帶有詳細註釋的 Sentinel 配置檔案示例。
執行一個 Sentinel 所需的最少配置如下所示:

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5

第一行配置指示 Sentinel 去監視一個名為 mymaster 的主伺服器, 這個主伺服器的 IP 地址為 127.0.0.1 , 埠號為 6379 , 而將這個主伺服器判斷為失效至少需要 2(quorum) 個 Sentinel 同意 (只要同意 Sentinel 的數量不達標,自動故障遷移就不會執行)。

不過要注意, 無論你設定要多少個 Sentinel 同意才能判斷一個伺服器失效, 一個 Sentinel 都需要獲得系統中多數(majority) Sentinel 的支援, 才能發起一次自動故障遷移, 並預留一個給定的配置紀元 (configuration Epoch ,一個配置紀元就是一個新主伺服器配置的版本號)。

換句話說, 在只有少數(minority) Sentinel 程序正常運作的情況下, Sentinel 是不能執行自動故障遷移的。

哨兵模式執行機制

image.png

這個是經典的3節點哨兵叢集,我們配置quorum = 2,3個節點的majority也是2,如果M1所在的機器宕機了,那麼三個哨兵還剩下2個,S2和S3可以一致認為mater宕機,然後選舉一個來執行故障轉移

同時majority也是2,這個2個哨兵可以允許執行故障轉移的。

這裡再說下,如果是兩節點哨兵,類似:

image.png

配置的quorum = 1,master宕機,s1和s2中只要有一個哨兵認為master宕機就可以進行切換,同時s1和s2中選舉一個哨兵來執行故障轉移

但是此時majority=2,至少有2個或者的哨兵才能夠允許執行故障轉移,此時只有R1,故障是不會允許轉移的

允許故障轉移的條件時:或者的sentinal節點數>=majority

這裡再說兩個概念:

  1. quorum:sentinal叢集中會配置該引數,例如5臺機器,配置的quorum為3,那麼如果有3個節點認為master宕機了,sentinal叢集就會認為master宕機了,每個節點認為宕機被稱為主觀宕機sdown,sentinal叢集認為master宕機被稱為客觀宕機odown
  2. majority 代表大多數的意思,例如2的majority=2,3的majority=2,4的majority=2,5的majority=3

哨兵模式下主備切換資料丟失問題

主備切換過程中,有兩種情形會導致資料丟失:

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

  2. 腦裂導致的資料丟失
    腦裂就是說某個master所在的機器脫離了正常網路,跟其他slave機器不能連線,但是實際上master還執行著,此時哨兵可能就會認為master宕機了,然後開啟選舉,將其他slave切換成了master。

    這個時候叢集中就會有兩個master,也就是所謂的腦裂

    此時雖然某個slave被切換成了master,但是可能client還沒來得及切換到新的master,還繼續向舊的master寫資料,這部分資料可能會丟失

    因此舊的master再次恢復的時候,會被作為一個slave掛到新的master上去,自己的資料會被清空,重新從新的master複製資料

03_Redis哨兵模式下腦裂問題.jpg

哨兵模式資料丟失問題解決方案

從 Redis 2.8 開始, 為了保證資料的安全性, 可以通過配置, 讓主伺服器只在有至少 N 個當前已連線從伺服器的情況下, 才執行寫命令。

不過, 因為 Redis 使用非同步複製, 所以主伺服器傳送的寫資料並不一定會被從伺服器接收到, 因此, 資料丟失的可能性仍然是存在的。

以下是這個特性的運作原理:

  • 從伺服器以每秒一次的頻率 PING 主伺服器一次, 並報告複製流的處理情況。

  • 主伺服器會記錄各個從伺服器最後一次向它傳送 PING 的時間。

  • 使用者可以通過配置, 指定網路延遲的最大值 min-slaves-max-lag , 以及執行寫操作所需的至少從伺服器數量 min-slaves-to-write

如果至少有 min-slaves-to-write 個從伺服器, 並且這些伺服器的延遲值都少於 min-slaves-max-lag 秒, 那麼主伺服器就會執行客戶端請求的寫操作。

你可以將這個特性看作 CAP 理論中的 C 的條件放寬版本: 儘管不能保證寫操作的永續性, 但起碼丟失資料的視窗會被嚴格限制在指定的秒數中。

另一方面, 如果條件達不到 min-slaves-to-writemin-slaves-max-lag 所指定的條件, 那麼寫操作就不會被執行, 主伺服器會向請求執行寫操作的客戶端返回一個錯誤。

以下是這個特性的兩個選項和它們所需的引數:

-min-slaves-to-write <number of slaves>

  • min-slaves-max-lag

詳細的資訊可以參考 Redis 原始碼中附帶的 redis.conf 示例檔案。

上面兩個配置可以減少非同步複製和腦裂導致的資料丟失

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

  2. 減少腦裂的資料丟失
    如果一個master出現了腦裂,跟其他slave丟了連線,那麼上面兩個配置可以確保說,如果不能繼續給指定數量的slave傳送資料,而且slave超過10秒沒有給自己ack訊息,那麼就直接拒絕客戶端的寫請求

這樣腦裂後的舊master就不會接受client的新資料,也就避免了資料丟失

上面的配置就確保了,如果跟任何一個slave丟了連線,在10秒後發現沒有slave給自己ack,那麼就拒絕新的寫請求

因此在腦裂場景下,最多就丟失10秒的資料。

哨兵模式其他一些機制原理分析

sdown和odown轉換機制

sdown(subjectively down)和odown(objectively down)是年終失敗狀態:

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

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

例如我們可以配置:

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

這裡就代表如果ping master超過了5s鍾就認為master sdown

哨兵叢集的自動發現機制

一個 Sentinel 可以與其他多個 Sentinel 進行連線, 各個 Sentinel 之間可以互相檢查對方的可用性, 並進行資訊交換。

你無須為執行的每個 Sentinel 分別設定其他 Sentinel 的地址, 因為 Sentinel 可以通過釋出與訂閱功能來自動發現正在監視相同主伺服器的其他 Sentinel , 這一功能是通過向頻道__sentinel__:hello 傳送資訊來實現的。

與此類似, 你也不必手動列出主伺服器屬下的所有從伺服器, 因為 Sentinel 可以通過詢問主伺服器來獲得所有從伺服器的資訊。

  • 每個 Sentinel 會以每兩秒一次的頻率, 通過釋出與訂閱功能, 向被它監視的所有主伺服器和從伺服器的__sentinel__:hello頻道傳送一條資訊, 資訊中包含了 Sentinel 的 IP 地址、埠號和執行 ID (runid)。

  • 每個 Sentinel 都訂閱了被它監視的所有主伺服器和從伺服器的 __sentinel__:hello 頻道, 查詢之前未出現過的 sentinel (looking for unknown sentinels)。 當一個 Sentinel 發現一個新的 Sentinel 時, 它會將新的 Sentinel 新增到一個列表中, 這個列表儲存了 Sentinel 已知的, 監視同一個主伺服器的所有其他 Sentinel 。

  • Sentinel 傳送的資訊中還包括完整的主伺服器當前配置(configuration)。 如果一個 Sentinel 包含的主伺服器配置比另一個 Sentinel 傳送的配置要舊, 那麼這個 Sentinel 會立即升級到新配置上。

  • 在將一個新 Sentinel 新增到監視主伺服器的列表上面之前, Sentinel 會先檢查列表中是否已經包含了和要新增的 Sentinel 擁有相同執行 ID 或者相同地址(包括 IP 地址和埠號)的 Sentinel , 如果是的話, Sentinel 會先移除列表中已有的那些擁有相同執行 ID 或者相同地址的 Sentinel , 然後再新增新 Sentinel 。

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

own-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

接下來會對slave進行排序:

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

quorum和majority

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

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

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

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配置的

故障轉移

一次故障轉移操作由以下步驟組成:

  • 發現主伺服器已經進入客觀下線狀態。
  • 對我們的當前紀元進行自增(詳情請參考 Raft leader election ), 並嘗試在這個紀元中當選。
  • 如果當選失敗, 那麼在設定的故障遷移超時時間的兩倍之後, 重新嘗試當選。 如果當選成功, 那麼執行以下步驟。
  • 選出一個從伺服器,並將它升級為主伺服器。
  • 向被選中的從伺服器傳送 LAVEOF NO ONE命令,讓它轉變為主伺服器。
  • 通過釋出與訂閱功能, 將更新後的配置傳播給所有其他 Sentinel , 其他 Sentinel 對它們自己的配置進行更新。
  • 向已下線主伺服器的從伺服器傳送SLAVEOF host port命令, 讓它們去複製新的主伺服器。
  • 當所有從伺服器都已經開始複製新的主伺服器時, 領頭 Sentinel 終止這次故障遷移操作。

參考自:
http://redisdoc.com/topic/sentinel.html?highlight=master%20down%20after%20millisec