Redis基礎篇(七)哨兵機制

大雜草發表於2021-01-04

上一篇文章介紹了高可靠方案:主從叢集模式。通過主從庫的讀寫分離,來保證服務的可靠性。

當某個從庫出現故障時,不影響服務的使用,主庫仍然可以處理寫命令,其他從庫可以處理讀命令。但主庫發生故障,就不能處理寫命令了,從庫只能處理讀命令。這就影響服務的正常使用了,該如何解決呢?

只要找一個從庫當主庫就可以解決了。但還有三個問題需要處理:

  1. 主庫真的掛了嗎?
  2. 該選擇哪個從庫作為主庫?
  3. 怎麼把新主庫的相關資訊通知給從庫和客戶端?

這裡就要介紹Redis的哨兵機制了。哨兵機制指在Redis主從叢集模式下,實現主從庫自動切換。它有效地解決了主從叢集模式下故障時的三個問題。

哨兵機制的基本流程

哨兵其實是一個執行在特殊模式下的Redis進行,主從庫例項執行的同時,它也在執行。哨兵主要負責的任務有三個:監牢、選主(選擇主庫)和通知。如下圖所示:

哨兵的基本流程

  • 監控:哨兵程式週期性地給所有主從庫傳送PING命令,檢測它們是否線上執行
  • 選主(選擇主庫):選出新主庫
  • 通知:讓從庫執行replicaof,與新主庫同步;通知客戶端,與新主庫連線。

通知任務相對比較簡單且容易理解,但在監控和選主這兩個任務中,哨兵需要做出兩個決策:

  • 在監控任務中,哨兵需要判斷主庫是否處於下線狀態;
  • 在選主任務中,哨兵要決定選擇哪個從庫例項作為主庫。

下面就介紹一下這兩個任務。

哨兵是如何判斷主庫是否下線

哨兵對主庫的下線狀態判斷有“主觀下線”和“客觀下線”兩種。

  • 主線下線,哨兵發現主庫或從庫對PING命令的響應超時。
  • 客觀下線,表示主庫下線是一個客觀事實。

哨兵在判斷主從庫採用不同方式:

  • 哨兵PING從庫,如果超時,就直接標記“主觀下線”
  • 哨兵PING主庫,超時不能直接標記“主觀下線”,因為可能由於網路阻塞等原因導致誤判。

如何解決哨兵誤判?

通過哨兵叢集,也就是由多個哨兵組成的叢集來進行判斷。採用少數服從多數,超過N/2+1判斷主庫為“主觀下線”,那就判斷主庫為“客觀下線”。關於哨兵叢集,下面會詳細介紹。

哨兵是如何選定新主庫的?

哨兵選定新主庫用四個字概括:“篩選+打分”。簡單來說,就是根據篩選條件選出候選從庫,然後通過打分,選出最高分的作為新主庫。下面說一下篩選條件和打分規則。

篩選條件

首先從庫必須線上執行。

其次從庫網路狀態良好。從庫和主庫斷連超出一定的閾值就把這個從庫篩掉。這個閾值就是down-after-milliseconds,表示主從庫斷連的最大連線超時時間。例如發生超過10次,就認定從庫的網路狀況不好。

關於down-after-milliseconds,值越小,哨兵就越敏感。當網路擁塞但主庫正常,可能會發生不必要的切換。而當主庫真的故障了,就切換及時,對業務影響最小。

因此down-after-milliseconds要設定合適的值,既減少不必要的切換,也保證能夠及時切換,降低對業務的影響。

打分規則

第一輪,優先順序最高的從庫得分高。可通過slave-priority配置項,設定從庫優先順序。

第二輪,和舊主庫同步程度最接近的從庫得分高。很容易理解,越接近主庫,說明資料丟失越少。前面介紹主從複製時,已經知道主從庫是通過repl_backlog_buffer保持同步的,所以slave_repl_offset最接近master_repl_offset,得分高。

第三輪,ID號小的從庫得分高。每個例項都會有一個ID。這是最後一輪,必須決出勝負,所以就選擇最小ID的作為新主庫。

到這裡,我們對哨兵機制的基本流程有了一個整體的認識,下面我們再來了解關於主從切換的兩個問題。

在做主從切換時,客戶端能否正常地進行請求操作?

如果客戶端使用了讀寫分離,那麼讀請求不受影響,而寫請求會失敗。失敗持續時間 = 哨兵切換主從的時間 + 客戶端感知到新主庫的時間。

如果不想讓業務感知到異常,那客戶端只能把寫失敗的請求先快取起來,或者寫入訊息佇列中介軟體。這種只適合對寫入請求返回值不敏感的業務。

應用程式不感知服務中斷,哨兵和客戶端要做什麼?

當哨兵完成主從切換後,客戶端需要及時感知到主庫發生了變更,然後把快取的寫請求寫入到新庫中,保證後續寫請求不會再受到影響。具體做法有兩方面:

一方面是哨兵主要通知客戶端。哨兵在選主後,會把新主庫的地址寫入自己例項的pub/sub裡,客戶端需要訂閱pub/sub。當切換新主庫後,客戶端就能拿到新主庫的地址,把寫請求發到這個新主庫即可。

另一方面是客戶端主動獲取最新的主從地址。如果客戶端因為某些原因錯過了哨兵的通知,或者哨兵通知後客戶端處理失敗了,就需要客戶端主動獲取。

下面再來學習哨兵叢集。

哨兵叢集

如果哨兵例項在執行時發生故障,主從庫還能正常切換嗎?

通常,我們在解決一個系統問題的時候,會引入一個新機制,或者設計一層新功能。這裡Redis引入哨兵叢集來解決哨兵例項的高可靠性問題。

基於pub/sub機制組成哨兵叢集

哨兵例項之間可以互相發現,靠的是Redis提供的pub/sub機制,也就是釋出/訂閱機制。

哨兵和主庫建立連線,就可以在主庫上釋出訊息了,比如釋出自己的連線資訊(IP和埠),同時它也會從主庫上訂閱訊息,獲得其他哨兵的連線資訊。

當多個哨兵例項都在主庫上做了釋出和訂閱操作,它們之間就能知道彼此的IP地址和埠了。

哨兵除了彼此之間建立起連線形成叢集外,還需要和從庫建立連線。因為在哨兵的監控任務中,它需要對主從庫都進行監控,而且在主從庫切換完成後,還需要通知從庫,讓它們和新主庫進行同步。

那麼,哨兵是如何知道從庫的IP地址和埠的呢?

哨兵通過向主庫傳送INFO命令來完成的。如下圖所示:

image

通過pub/sub機制,哨兵之間可以組成叢集;通過INFO命令,哨兵和從庫建立連線,並進行監控。

但是哨兵不能只和主、從庫連線。因為,主從庫切換後,客戶端也需要知道新主庫的連線資訊。所以哨兵還需要把新主庫的資訊告訴客戶端。

還是可以依賴pub/sub機制來完成哨兵和客戶端的資訊同步。

基於pub/sub機制的客戶端事件通知

本質上,哨兵就是一個執行在特定模式下的Redis例項,只完成監控、選主和通知的任務,因此它也具有pub/sub功能。下面是哨兵提供的一些重要的頻道。

哨兵提供的重要頻道

知道這些頻道後,可以讓客戶端從哨兵這裡訂閱訊息了。

由哪個哨兵執行主從切換?

在哨兵叢集模式下,通過“投標仲裁”,選出哨兵Leader來執行主從切換。投標仲裁的流程如下圖所示:

image

任何一個例項判斷主庫“主觀下線”,就會給其他例項傳送is-master-down-by-addr命令。

其他例項會根據自己和主庫的連線情況,做出Y或N響應。

一個哨兵獲得仲裁所需的贊同票數後,就可以標記主庫為“客觀下線”。這個票數可以通過哨兵配置檔案中的quorum配置項來設定。

再給其他哨兵傳送命令,表示希望由自己來執行主從切換,並讓其他哨兵進行投票。這個也叫“Leader選舉”,滿足以下兩個條件才能當Leader:

  • 拿到半數以上的票數
  • 拿到的票數>=quorum

小結

  • 哨兵機制是實現Redis不間斷服務的保證。
  • 哨兵機制的三大任務:監控、選主、通知。
  • 為了降低誤判率,通過採用哨兵叢集,並採用“少數服從多數”的原則,判斷主庫是否客觀下線。
  • 哨兵叢集的關鍵機制,包括:
    • 基於pub/sub機制的哨兵叢集組成過程;
    • 基於INFO命令的從庫列表,這可以幫助哨兵和從庫建立連線;
    • 基於哨兵自身的pub/sub功能,實現了客戶端的哨兵之間的事件通知。
  • 哨兵叢集在判斷了主庫“客觀下線”後,經過投票仲裁,選舉一個Leader來負責主從切換。

最後再分分享一個經驗:要保證所有哨兵例項的配置是一致的,尤其是主觀下線的判斷值down-after-milliseconds

參考資料

相關文章