天天看點

(七)哨兵機制:主庫挂了,如何不間斷服務?

作者:愛吃魚的程式猿

上節課,我們學習了主從叢集模式。在這個模式下,如果從庫發生故障了,用戶端可以繼續向主庫或其他庫發送請求,進行相關的操作,但是如果主庫發生故障了,那就直接會影響從庫的同步,因為從庫沒有可用的主庫進行資料複制操作了。

而且,如果用戶端發送的都是讀操作請求,那還可以由從庫繼續提供服務,這在純讀的業務場景下還能接受。但是,一旦有寫操作請求了,按照主從模式下的讀寫分離要求,需要由主庫來完成寫操作。此時,也沒有執行個體可以來服務用戶端的寫請求了,如下圖所示:

(七)哨兵機制:主庫挂了,如何不間斷服務?

主庫故障後從庫無法服務寫操作

無論是寫服務中斷,還是從庫無法進行資料同步,都是不能接受的。是以,如果主庫挂了,我們就需要運作一個新主庫,比如說把一個從庫切換為主庫,把它當成主庫。這就涉及到三個問題:

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

這就要提到哨兵機制了。在Redis主從叢集中,哨兵機制是實作主從自動切換的關鍵機制,它有效的解決了主從複制模式下故障轉移的這三個問題。

接下來,我們一起來學習哨兵機制。

哨兵機制的基本流程

哨兵其實就是一個運作在特殊模式下的Redis程序,主從庫執行個體運作的同時,它也在運作。哨兵主要負責的就是三個任務:監控、選主(選擇主庫)和通知。

我們先看監控。監控是指哨兵程序在運作時,周期性的給所有主從庫發送PING指令,檢測它們是否仍然線上運作。如果從庫沒有在規定時間内響應哨兵發送的PING指令,哨兵就會把它标記為“下線狀态”;同樣,如果主庫也沒有在規定時間内響應哨兵的PING指令,哨兵就會判斷主庫下線,然後開始自動切換主庫的流程。

這個流程首先是執行哨兵的第二個任務,選主。主庫挂了以後,哨兵就需要從很多個從庫裡,按照一定的規則選擇一個從庫執行個體,把它作為新的主庫。這一步完成後,現在的叢集裡就有了新主庫。

然後,哨兵會執行最後一個任務,通知。在執行通知任務時,哨兵會把新主庫的連接配接資訊發給其它從庫,讓它們執行replicaof指令,和新主庫建立連結,并進行資料複制。同時,哨兵會把新主庫的連接配接資訊通知給用戶端,讓它們把請求操作發送到新主庫上。

我畫了一張圖,展示了這三個任務以及它們各自的目标。

(七)哨兵機制:主庫挂了,如何不間斷服務?

哨兵機制的三項任務與目标

在這三個任務中,通知任務相對來說比較簡單,哨兵隻需要把新主庫資訊發送給用戶端和從庫,讓它們和新主庫建立連接配接就行,并不涉及到決策邏輯。但是,在監控和選主這兩個任務中,哨兵需要做出兩個決策:

  • 在監控任務中,哨兵需要判斷主庫是否處于下線狀态;
  • 在選主任務中,哨兵也要決定選哪個從庫執行個體作為新主庫。

接下來,我們就先說說如何判斷主庫的下線狀态。

你首先要知道的是,哨兵對主庫的下線判斷有“主觀下線”和“客觀下線”兩種。那麼,為什麼會存在兩種判斷呢?它們的差別和聯系是什麼?

主觀下線和客觀下線

哨兵程序會使用PING指令來檢查它自己和主、從庫的網絡連接配接情況,用來判斷執行個體的狀态。如果哨兵發送主庫或從庫對PING指令的響應逾時了,那麼,哨兵就會先把它标記為“主觀下線”。

如果檢測的是從庫,那麼,哨兵簡單的把它标記為“主觀下線”就行了,因為從庫的下線影響一般不太大,叢集的對外服務不會間斷。

但是,如果檢測的是主庫,那麼,哨兵還不能簡單的把它标記為“主觀下線”,開啟主從切換。因為很有可能存在這麼一個情況:那就是哨兵誤判了,其實主庫并沒有故障。可是,一旦啟動了主從切換,後續的選主和通知操作都會帶來額外的計算和通信開銷。

為了避免這些不必要的開銷,我們要特别注意誤判的情況。

首先,我們要知道啥叫誤判。很簡單,就是主庫實際并沒有下線,但是哨兵誤以為它下線了。誤判一般會發生在叢集網絡壓力較大、網絡擁塞,或者是主庫本身壓力較大的情況下。

一旦哨兵判斷主庫下線了,就會開始選擇新主庫,并讓從庫和新主庫進行資料同步,這個過程本身就會有開銷,例如,哨兵要花時間選出新主庫,從庫也需要花時間和新主庫同步。而在誤判的情況下,主庫本身根本就不需要進行切換的,是以這個過程的開銷是沒有價值的。正因為這樣,我們需要判斷是否有誤判,以及減少誤判。

那麼怎麼減少誤判呢?在日常生活中,當我們要對一些重要的事情做判斷的時候,經常會和家人或朋友商量一下,然後再做決定。

哨兵機制也是類似,它通常會采用多執行個體的叢集模式進行部署,這也被稱為哨兵叢集。引入多個哨兵執行個體一起來判斷,就可以避免單個哨兵因自身網絡不好,而誤判主庫下線的情況。同時,多個哨兵網絡同時不穩定的機率較小,由它們一起做決策,誤判率也能降低。

這節課,你隻需要了解哨兵叢集在減少誤判方面的作用就行了,至于具體的運作機制,下節課我們再重點學習。

在判斷主庫是否下線時,不能由一個哨兵說了算,隻有大多數的哨兵執行個體,都判斷主庫已經“主觀下線”了,主庫才會标記為“客觀下線”,這個叫法也是表明主庫下線成為一個客觀事實了。這個判斷原則就是:少數服從多數。同時,這會進一步觸發哨兵開始主從切換流程。

為了友善你了解,看下圖展示的邏輯。

(七)哨兵機制:主庫挂了,如何不間斷服務?

客觀下線的判斷

如圖所示,Redis主從叢集有一個主、三個從庫,還有三個哨兵執行個體。在圖檔的左邊,哨兵2判斷主庫為“主觀下線”,但哨兵1和3卻判定主庫是上線狀态,此時,主庫仍然被判斷為處于上線狀态。在圖檔的右邊,哨兵1和2都判斷主庫為“主觀下線”,此時,即使哨兵3仍然判斷主庫為上線狀态,主庫也被标記為“客觀下線”了。

簡單來說,“客觀下線”的标準就是,當有N個哨兵執行個體時,最好要有(N/2+1)個執行個體判斷主庫為“主觀下線”,才能最終判斷主庫“客觀下線”。這樣一來,就可以減少誤判的機率,也能避免誤判帶來的無謂的主從切換。(當然,有多少個執行個體做出“客觀下線”的判斷才可以,可以由Redis管理者自行設定)。

好了,到這裡,你可以看到,借助于多個哨兵執行個體的共同判斷機制,我們就可以更準确的判斷出主庫是否處于下線狀态。如果主庫的确下線了,哨兵就要開始下一個決策過程了,即從許多從庫中,選出一個從庫來做新主庫。

如何標明新主庫?

一般來說,我把哨兵選擇新主庫的過程稱為“篩選+打分”。簡單來說,我們在多個從庫中,先按照一定的篩選條件,把不符合條件的從庫去掉。然後,我們再按照一定的規則,給剩下的從庫逐個打分,将得分最高的從庫選為新主庫,如下圖所示:

(七)哨兵機制:主庫挂了,如何不間斷服務?

新主庫的選擇過程

在剛剛的這段話裡,需要注意的是兩個字“一定”,現在,我們要考慮這裡的“一定”具體是指什麼。

首先來看篩選的條件。

一般情況下,我們肯定要先保證所選的從庫仍然線上運作。不過,在選主時從庫正常線上,這隻能表示從庫的現狀良好,并不代表它就是最适合做主庫的。

設想一下,如果在選主時,一個從庫正常運作,我們把它選為新主庫開始使用了。可是,很快它的網絡出了故障,此時,我們就得重新選主了。這顯然不是我們期望的結果。

是以,在選主時,除了要檢查從庫目前的線上狀态,還要判斷它之前的網絡連接配接狀态。如果從庫總是和主庫斷連,而且斷連次數超過了一定的門檻值,我們就有理由相信,這個從庫的網絡狀況并不是太好,就可以把這個從庫篩選掉了。

具體怎麼判斷呢?你使用配置項down-after-milliseconds*10。其中down-after-milliseconds是我們認定主從庫斷連的最大連接配接逾時時間。如果在donw-after-milliseconds毫秒内,主從節點都沒有通過網絡聯系上,我們就可以認為主從節點斷連了。如果發生斷連的次數超過了10次,就說明這個從庫的網絡狀況不太好,不适合作為新主庫。

好了,這樣我們就過濾了不适合做主庫的從庫,完成了篩選工作。

接下來就要給剩餘的從庫打分了。我們可以分别按照三個規則依次進行三輪打分,這三個規則分别是從庫優先級、從庫複制進度、從庫ID号。隻要在某一輪中,有從庫得分最高,那麼它就是從庫了,選主過程到此結束。如果沒有出現得分最高的從庫,那麼就繼續進行下一輪。

第一輪:優先級最高的從庫得分高。

使用者可以通過slave_priority配置項,給不同的從庫設定不同的優先級。比如,你有兩個從庫,它們的記憶體大小不一樣,你可以手動給記憶體大的執行個體設定高的優先級。在選主時,哨兵會給優先級高的從庫打高分,如果有一個從庫優先級最高,那麼它就是新主庫了。如果從庫優先級都一樣,那麼哨兵開始第二輪打分。

第二輪:和舊主庫複制進度最接近的從庫得分高。

這個規則的依據是,如果選主和舊主庫同步最接近的從庫作為主庫,那麼,這個新主庫上就有最新的資料。

如何判斷從庫和舊主庫間的同步進度呢?

上節課我向你介紹過,主從庫同步時有個指令傳播的過程。在這個過程中,主庫會用master_repl_offset 記錄目前的最新操作在 repl_backlog_buffer 中的位置,而從庫會用 slave_repl_offset 這個值記錄目前的複制進度。

此時,我們想要找的從庫,它的slave_repl_offset 需要最接近 master_repl_offset。如果在所有從庫中,有從庫的 slave_repl_offset 最接近 master_repl_offset,那麼它的得分就最高,可以作為新主庫。

就像下圖所示,舊主庫的 master_repl_offset 是1000,從庫1、2和3的slave_repl_offset分别是950、990、900,那麼,從庫2就應該被選為新主庫。

(七)哨兵機制:主庫挂了,如何不間斷服務?

基于複制進度的新主庫選主原則

第三輪:ID号小的從庫得分最高。

每個執行個體都會有一個ID,這個ID就類似于這裡從庫的編号。目前,Redis在選主庫時,有一個預設的規定:在優先級和複制進度相同的情況下,ID号最小的從庫得分最高,會被選為新主庫。

到這裡,新主庫就被選出來了,“選主” 這個過程就完成了。

我們再回顧下這個流程。首先,哨兵會按照線上狀态,網絡狀态,篩選過濾掉一部分不符合要求的從庫,然後,依次按照優先級、複制進度,ID号大小再對剩餘的從庫進行打分,隻要有得分最高的從庫出現,就把它選為新主庫。

小結

這節課,我們一起學習了哨兵機制,它是實作Redis不間斷服務的重要保證。具體來說,主從叢集的資料同步,是資料可靠性的基礎保證;而在主庫發生故障時,自動的主從切換是服務不間斷的關鍵支撐。

Redis 的哨兵機制自動完成了以下三大功能,進而實作了主從庫的自動切換,可以降低 Redis 叢集的運維開銷:

  • 監控主庫運作狀态,并判斷主庫是否客觀下線;
  • 在主庫客觀下線後,選取新主庫;
  • 選出新主庫後,通知從庫和用戶端;

為了降低誤判率,在實際應用時,哨兵機制通常采用多執行個體方式進行部署,多個哨兵執行個體通過“少數服從多數”的原則,來判斷主庫是否客觀下線。一般來說,我們可以部署三個哨兵,如果有兩個哨兵判定主庫“主觀下線”,就可以開始切換過程。當然,如果你希望進一步提升判斷準确率,也可以再适當增加哨兵個數,比如說使用五個哨兵。

但是,使用多個哨兵執行個體來降低誤判率,其實相當于組成了一個哨兵叢集,我們會是以面臨着一些新的挑戰,例如:

  • 哨兵叢集中有執行個體挂了怎麼辦,會影響主庫判斷和選主嗎?
  • 哨兵叢集多數執行個體達成共識,判斷出主庫“客觀下線”後,由哪個執行個體來執行主從切換呢?

要搞懂這些問題,就不得不提哨兵叢集了,下節課,我們來具體聊賴哨兵叢集的機制和問題。