Redis哨兵叢集:哨兵掛了,主從庫還能切換嗎?

資料庫工作筆記發表於2023-10-27

來源:碼農本農

在前一篇文章中,我們深入研究了哨兵機制,它的作用是實現主從庫的自動切換。透過部署多個哨兵例項,我們構建了一個哨兵叢集,這個叢集中的多個例項共同協作,以降低對主庫下線的誤判率。

然而,還有一個重要問題需要考慮:如果哨兵叢集中的某個例項發生故障,主從庫是否能夠繼續正常切換呢?

實際上,一旦多個例項組成了哨兵叢集,即使有個別哨兵例項出現故障而無法正常執行,其他健康的哨兵例項仍然能夠繼續協同工作,完成主從庫切換的各項任務,包括判斷主庫的下線狀態、選擇新的主庫,以及通知從庫和客戶端。

如果你曾經部署過哨兵叢集,你會發現,在配置哨兵資訊時,我們只需要指定主庫的 IP 和埠,而無需明確配置其他哨兵例項的連線資訊。這是因為哨兵叢集中的各個例項會相互感知和發現,形成一種自動協作的機制。


sentinel monitor <master-name> <ip> <redis-port> <quorum>

這些哨兵例項既然都不知道彼此的地址,又是怎麼組成叢集的呢?要弄明白這個問題,我們就需要學習一下哨兵叢集的組成和執行機制了。

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

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

哨兵只要和主庫建立起了連線,就可以在主庫上釋出訊息了,比如說釋出它自己的連線資訊(IP 和埠)。同時,它也可以從主庫上訂閱訊息,獲得其他哨兵釋出的連線資訊。當多個哨兵例項都在主庫上做了釋出和訂閱操作後,它們之間就能知道彼此的 IP 地址和埠。

除了哨兵例項,我們自己編寫的應用程式也可以透過 Redis 進行訊息的釋出和訂閱。所以,為了區分不同應用的訊息,Redis 會以頻道的形式,對這些訊息進行分門別類的管理。所謂的頻道,實際上就是訊息的類別。當訊息類別相同時,它們就屬於同一個頻道。反之,就屬於不同的頻道。只有訂閱了同一個頻道的應用,才能透過釋出的訊息進行資訊交換

在主從叢集中,主庫上有一個名為“__sentinel__:hello”的頻道,不同哨兵就是透過它來相互發現,實現互相通訊的。

我來舉個例子,具體說明一下。在下圖中,哨兵 1 把自己的 IP(172.16.19.3)和埠(26579)釋出到“__sentinel__:hello”頻道上,哨兵 2 和 3 訂閱了該頻道。那麼此時,哨兵 2 和 3 就可以從這個頻道直接獲取哨兵 1 的 IP 地址和埠號。

然後,哨兵 2、3 可以和哨兵 1 建立網路連線。透過這個方式,哨兵 2 和 3 也可以建立網路連線,這樣一來,哨兵叢集就形成了。它們相互間可以透過網路連線進行通訊,比如說對主庫有沒有下線這件事兒進行判斷和協商。

Redis哨兵叢集:哨兵掛了,主從庫還能切換嗎?

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

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

這是由哨兵向主庫傳送 INFO 命令來完成的。就像下圖所示,哨兵 2 給主庫傳送 INFO 命令,主庫接受到這個命令後,就會把從庫列表返回給哨兵。接著,哨兵就可以根據從庫列表中的連線資訊,和每個從庫建立連線,並在這個連線上持續地對從庫進行監控。哨兵 1 和 3 可以透過相同的方法和從庫建立連線。

Redis哨兵叢集:哨兵掛了,主從庫還能切換嗎?

透過 pub/sub 機制,哨兵之間可以形成一個協作叢集。此外,哨兵還能透過 INFO 命令獲得從庫的連線資訊,建立連線並進行監控。

然而,哨兵的連線工作不僅僅限於主庫和從庫。當主從庫發生切換後,客戶端需要獲取新主庫的連線資訊,以便繼續傳送請求操作。因此,哨兵的任務還包括將新主庫的資訊傳達給客戶端。

在實際使用哨兵時,有時候我們需要解決這樣的問題:如何讓客戶端能夠透過監控來了解哨兵進行主從切換的進度?具體來說,客戶端需要了解主從切換進行到哪個步驟了。這就意味著客戶端需要獲取有關哨兵叢集在監控、選主和切換等過程中發生的各種事件的資訊。

在這種情況下,我們仍然可以藉助 pub/sub 機制,以實現哨兵和客戶端之間的資訊同步。這種機制可以幫助客戶端跟蹤主從切換的各個步驟。

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

從根本上說,哨兵就是一個以特定模式執行的 Redis 例項。然而,它不會處理請求操作,而是專注於監控、選主和通知的任務。每個哨兵例項也提供 pub/sub 機制,允許客戶端透過訂閱訊息來獲取資訊。這些訊息訂閱頻道有眾多,各自包含了主從庫切換過程中的不同重要事件。

考慮到頻道眾多,嘗試同時理解它們可能會讓人感到不知所措。為了幫助你更輕鬆地理解,我將彙總一些關鍵頻道,這些頻道涵蓋了幾個重要事件,包括主庫下線判斷、新主庫的選定和從庫的重新配置。

Redis哨兵叢集:哨兵掛了,主從庫還能切換嗎?

知道了這些頻道之後,你就可以讓客戶端從哨兵這裡訂閱訊息了。具體的操作步驟是,客戶端讀取哨兵的配置檔案後,可以獲得哨兵的地址和埠,和哨兵建立網路連線。然後,我們可以在客戶端執行訂閱命令,來獲取不同的事件訊息。

舉個例子,你可以執行如下命令,來訂閱“所有例項進入客觀下線狀態的事件”:


SUBSCRIBE +odown

當然,你也可以執行如下命令,訂閱所有的事件:


PSUBSCRIBE  *

當哨兵把新主庫選擇出來後,客戶端就會看到下面的 switch-master 事件。這個事件表示主庫已經切換了,新主庫的 IP 地址和埠資訊已經有了。這個時候,客戶端就可以用這裡面的新主庫地址和埠進行通訊了


switch-master <master name> <oldip> <oldport> <newip> <newport>

有了這些事件通知機制,客戶端不僅可以獲取新主庫的連線資訊,還可以跟蹤主從庫切換過程中的各個重要事件。這為客戶端提供了切換的實時狀態,使其瞭解切換的進展情況。

現在,藉助 pub/sub 機制,哨兵與哨兵、哨兵與從庫、哨兵與客戶端之間都建立了緊密的聯絡。再加上之前我們介紹的主庫下線判定和新主庫選定的依據,哨兵叢集的監控、選主和通知三大任務基本已經可以正常執行了。不過,我們仍需考慮一個問題:在主庫發生故障後,哨兵叢集中有多個例項,那麼該由哪個哨兵來執行實際的主從切換呢?

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

確定由哪個哨兵來執行主從切換的過程,類似於主庫“客觀下線”的判斷過程,它也是一個“投票仲裁”的過程。在詳細探討這個過程之前,我們先回顧一下判斷主庫“客觀下線”的仲裁過程。

在哨兵叢集中,要斷定主庫“客觀下線”需要多個例項達成一致意見。我在之前的課程中分享了判斷“客觀下線”的原則,接下來,我們將深入探討這個具體的判斷過程。

一旦一個哨兵例項認為主庫“主觀下線”,它會向其他哨兵例項傳送 is-master-down-by-addr 命令。其他例項根據它們自身與主庫的連線情況,給出 Y 或 N 的響應。在這裡,Y 代表贊成,N 代表反對。

Redis哨兵叢集:哨兵掛了,主從庫還能切換嗎?

在獲得足夠的贊成票後,一個哨兵就可以將主庫標記為“客觀下線”。所需的贊成票數是透過哨兵配置檔案中的 quorum 配置項來設定的。舉個例子,如果現有 5 個哨兵,quorum 配置為 3,那麼一個哨兵需要獲得 3 張贊成票才能將主庫標記為“客觀下線”。這裡的 3 張贊成票包括該哨兵自己的一張票和其他兩個哨兵的贊成票。

當滿足了所需的贊成票數後,該哨兵會向其他哨兵傳送請求,表明自己希望執行主從切換,並請求進行投票,這個投票過程被稱為“Leader選舉”。因為最終執行主從切換的哨兵被稱為Leader,而投票過程則決定了誰將成為這個Leader。

在Leader選舉過程中,任何希望成為Leader的哨兵都必須滿足兩個條件:首先,必須獲得半數以上的贊成票;其次,獲得的票數還必須大於或等於哨兵配置檔案中的quorum值。以擁有3個哨兵為例,如果quorum設定為2,那麼任何想成為Leader的哨兵只需要獲得2張以上的贊成票即可。

這麼說你可能還不太好理解,我再畫一張圖片,展示一下 3 個哨兵、quorum 為 2 的選舉過程。

Redis哨兵叢集:哨兵掛了,主從庫還能切換嗎?

在T1時刻,S1判斷主庫已經“客觀下線”,並試圖成為Leader。首先,S1給自己投了一張贊成票,接著,S1傳送請求命令給S2和S3,表明它想成為Leader。

在T2時刻,S3也判斷主庫已經“客觀下線”並希望成為Leader。同樣地,S3首先給自己投了一張贊成票,之後向S1和S2傳送請求命令,表明它想成為Leader。

在T3時刻,S1收到了S3發來的Leader投票請求。因為S1已經投了一票贊成自己,所以無法再為其他哨兵投贊成票,於是S1回覆N,表示不同意。同時,S2在T3時刻接到S3的Leader投票請求,由於S2之前沒有進行投票,它會為首個向它傳送投票請求的哨兵回覆Y,而後續傳送請求的哨兵則會收到N。因此,在T3時刻,S2回覆S3,同意S3成為Leader。

在T4時刻,S2最終收到了T1時刻S1發來的投票請求。由於S2在T3時刻已經同意S3成為Leader,因此在T4時刻,S2回覆S1,不同意S1成為Leader。此情況出現的原因可能是S3和S2之間的網路通訊正常,而S1和S2之間的網路通訊出現了阻塞,導致投票請求傳輸緩慢。

最後,在T5時刻,S1收到的票數包括一張贊成票Y(來自自己)和一張反對票N(來自S2)。而S3除了自己的一張贊成票Y,還收到了S2的一張贊成票Y。在這一時刻,S3不僅獲得了半數以上的Leader贊成票,還達到了預設的quorum值(quorum為2),因此,S3最終成為Leader。接下來,S3將開始執行選主操作,並在選定新的主庫後,通知其他從庫和客戶端新主庫的資訊。

如果S3未獲得2張贊成票,那麼這輪投票將不會產生Leader。哨兵叢集將等待一段時間(即哨兵故障轉移超時時間的2倍),然後重新進行選舉。這是因為哨兵叢集的成功投票在很大程度上取決於選舉命令的正常網路傳播。如果網路負載較大或短時阻塞發生,可能導致沒有哨兵能夠獲得半數以上的贊成票。因此,在網路壓力減輕後重新進行選舉,將增加成功的機會。

需要注意的是,如果哨兵叢集只有2個例項,那麼一個哨兵要想成為Leader,必須獲得2張贊成票而不是1張。因此,如果有一個哨兵出現故障,那麼叢集將無法執行主從庫切換。因此,通常情況下,我們會配置至少3個哨兵例項,這一點非常重要,務必在實際應用中予以注意。

小結

一般情況下,當我們解決系統問題時,我們會引入新的機制或者設計新的功能層,就像我們在之前學習的內容一樣:為了實現主從切換,我們引入了哨兵機制;為了應對單個哨兵故障導致無法進行主從切換的情況,以及為了降低誤判率,我們引入了哨兵叢集;而哨兵叢集也需要一些機制來支援其正常執行。

本篇介紹了支援哨兵叢集的關鍵機制,包括:

  • 基於釋出/訂閱(pub/sub)機制的哨兵叢集組成過程;

  • 基於INFO命令的從庫列表,用於幫助哨兵與從庫建立連線;

  • 基於哨兵自身的釋出/訂閱功能,實現了客戶端和哨兵之間的事件通知。

在進行主從切換時,當然不是任何哨兵都可以隨意執行的,否則會造成混亂。因此,哨兵叢集需要經過投票仲裁來選舉出一個領導者,由它負責實際的主從切換,即負責選擇新的主庫並通知從庫和客戶端。

最後,我想分享一個經驗:要確保所有哨兵例項的配置保持一致,特別是主觀下線的判斷值 down-after-milliseconds。我們曾經因為這個值在不同的哨兵例項上配置不一致而遇到問題。這導致哨兵叢集無法達成對有故障的主庫的共識,最終導致叢集服務不穩定。因此,請務必注意這條看似簡單的經驗。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70027826/viewspace-2991421/,如需轉載,請註明出處,否則將追究法律責任。

相關文章