為什麼 Redis 要有哨兵機制?

小林coding 發表於 2022-05-19
Redis

作者:小林coding

計算機八股文刷題網站:https://xiaolincoding.com

大家好,我是小林。

這次聊聊,Redis 的哨兵機制。

提綱提綱

為什麼要有哨兵機制?

在 Redis 的主從架構中,由於主從模式是讀寫分離的,如果主節點(master)掛了,那麼將沒有主節點來服務客戶端的寫操作請求,也沒有主節點給從節點(slave)進行資料同步了。

主節點掛了主節點掛了

這時如果要恢復服務的話,需要人工介入,選擇一個「從節點」切換為「主節點」,然後讓其他從節點指向新的主節點,同時還需要通知上游那些連線 Redis 主節點的客戶端,將其配置中的主節點 IP 地址更新為「新主節點」的 IP 地址。

這樣也不太“智慧”了,要是有一個節點能監控「主節點」的狀態,當發現主節點掛了 ,它自動將一個「從節點」切換為「主節點」的話,那麼可以節省我們很多事情啊!

Redis 在 2.8 版本以後提供的哨兵(Sentinel)機制,它的作用是實現主從節點故障轉移。它會監測主節點是否存活,如果發現主節點掛了,它就會選舉一個從節點切換為主節點,並且把新主節點的相關資訊通知給從節點和客戶端。

哨兵機制是如何工作的?

哨兵其實是一個執行在特殊模式下的 Redis 程式,所以它也是一個節點。從“哨兵”這個名字也可以看得出來,它相當於是“觀察者節點”,觀察的物件是主從節點。

當然,它不僅僅是觀察那麼簡單,在它觀察到有異常的狀況下,會做出一些“動作”,來修復異常狀態。

哨兵節點主要負責三件事情:監控、選主、通知

哨兵的職責哨兵的職責

所以,我們重點要學習這三件事情:

  • 哨兵節點是如何監控節點的?又是如何判斷主節點是否真的故障了?
  • 根據什麼規則選擇一個從節點切換為主節點?
  • 怎麼把新主節點的相關資訊通知給從節點和客戶端呢?

如何判斷主節點真的故障了?

哨兵會週期性地給所有主從節點傳送 PING 命令,當主從節點收到 PING 命令後,會傳送一個響應命令給哨兵,這樣就可以判斷它們是否在正常執行。

哨兵監控主從節點哨兵監控主從節點

如果主節點或者從節點沒有在規定的時間內響應哨兵的 PING 命令,哨兵就會將它們標記為「主觀下線」。這個「規定的時間」是配置項 down-after-milliseconds 引數設定的,單位是毫秒。

主觀下線?難道還有客觀下線?

是的沒錯,客觀下線只適用於主節點。

之所以針對「主節點」設計「主觀下線」和「客觀下線」兩個狀態,是因為有可能「主節點」其實並沒有故障,可能只是因為主節點的系統壓力比較大或者網路傳送了擁塞,導致主節點沒有在規定時間內響應哨兵的 PING 命令。

所以,為了減少誤判的情況,哨兵在部署的時候不會只部署一個節點,而是用多個節點部署成哨兵叢集最少需要三臺機器來部署哨兵叢集),通過多個哨兵節點一起判斷,就可以就可以避免單個哨兵因為自身網路狀況不好,而誤判主節點下線的情況。同時,多個哨兵的網路同時不穩定的概率較小,由它們一起做決策,誤判率也能降低。

具體是怎麼判定主節點為「客觀下線」的呢?

當一個哨兵判斷主節點為「主觀下線」後,就會向其他哨兵發起命令,其他哨兵收到這個命令後,就會根據自身和主節點的網路狀況,做出贊成投票或者拒絕投票的響應。

為什麼 Redis 要有哨兵機制?

當這個哨兵的贊同票數達到哨兵配置檔案中的 quorum 配置項設定的值後,這時主節點就會被該哨兵標記為「客觀下線」。

例如,現在有 3 個哨兵,quorum 配置的是 2,那麼一個哨兵需要 2 張贊成票,就可以標記主節點為“客觀下線”了。這 2 張贊成票包括哨兵自己的一張贊成票和另外兩個哨兵的贊成票。

PS:quorum 的值一般設定為哨兵個數的二分之一加1,例如 3 個哨兵就設定 2。

哨兵判斷完主節點客觀下線後,哨兵就要開始在多個「從節點」中,選出一個從節點來做新主節點。

如何選新主節點?

那麼多「從節點」,到底選擇哪個從節點作為新主節點的?

隨機的方式好嗎?隨機的方式,實現起來很簡單,但是如果選到一個網路狀態不好的從節點作為新主節點,那麼可能在將來不久又要做一次主從故障遷移。

所以,我們首先要把網路狀態不好的從節點給過濾掉。首先把已經下線的從節點過濾掉,然後把以往網路連線狀態不好的從節點也給過濾掉。

怎麼判斷從節點之前的網路連線狀態不好呢?

Redis 有個叫 down-after-milliseconds * 10 配置項,其down-after-milliseconds 是主從節點斷連的最大連線超時時間。如果在 down-after-milliseconds 毫秒內,主從節點都沒有通過網路聯絡上,我們就可以認為主從節點斷連了。如果發生斷連的次數超過了 10 次,就說明這個從節點的網路狀況不好,不適合作為新主節點。

至此,我們就把網路狀態不好的從節點過濾掉了,接下來要對所有從節點進行三輪考察:優先順序、複製進度、ID 號。在進行每一輪考察的時候,哪個從節點優先勝出,就選擇其作為新主節點。

  • 第一輪考察:哨兵首先會根據從節點的優先順序來進行排序,優先順序越小排名越靠前,
  • 第二輪考察:如果優先順序相同,則檢視複製的下標,哪個從「主節點」接收的複製資料多,哪個就靠前。
  • 第三輪考察:如果優先順序和下標都相同,就選擇從節點 ID 較小的那個。

第一輪考察:優先順序最高的從節點勝出

Redis 有個叫 slave-priority 配置項,可以給從節點設定優先順序。

每一臺從節點的伺服器配置不一定是相同的,我們可以根據伺服器效能配置來設定從節點的優先順序。

比如,如果 「 A 從節點」的實體記憶體是所有從節點中最大的, 那麼我們可以把「 A 從節點」的優先順序設定成最高。這樣當哨兵進行第一輪考慮的時候,優先順序最高的 A 從節點就會優先勝出,於是就會成為新主節點。

第二輪考察:複製進度最靠前的從節點勝出

如果在第一輪考察中,發現優先順序最高的從節點有兩個,那麼就會進行第二輪考察,比較兩個從節點哪個複製進度。

什麼是複製進度?主從架構中,主節點會將寫操作同步給從節點,在這個過程中,主節點會用 master_repl_offset 記錄當前的最新寫操作在 repl_backlog_buffer 中的位置,而從節點會用 slave_repl_offset 這個值記錄當前的複製進度。

為什麼 Redis 要有哨兵機制?

如果某個從節點的 slave_repl_offset 最接近 master_repl_offset,說明它的複製進度是最靠前的,於是就可以將它選為新主節點。

第三輪考察:ID 號小的從節點勝出

如果在第二輪考察中,發現有兩個從節點優先順序和複製進度都是一樣的,那麼就會進行第三輪考察,比較兩個從節點的 ID 號,ID 號小的從節點勝出。

什麼是 ID 號?每個從節點都有一個編號,這個編號就是 ID 號,是用來唯一標識從節點的。

到這裡,選主的事情終於結束了。簡單給大家總結下:

  • 過濾掉已經離線的從節點;
  • 過濾掉歷史網路連線狀態不好的從節點;
  • 將剩下的從節點,進行三輪考察:優先順序、複製進度、ID 號。在每一輪考察過程中,如果找到了一個勝出的從節點,就將其作為新主節點。

由哪個哨兵進行主從故障轉移?

前面說過,為了更加“客觀”的判斷主節點故障了,一般不會只由單個哨兵的檢測結果來判斷,而是多個哨兵一起判斷,這樣可以減少誤判概率,所以哨兵是以哨兵叢集的方式存在的。

那在選定了即將作為主節點的從節後,由哨兵叢集中的哪個節點進行主從故障轉移呢?

所以這時候,還需要在哨兵叢集中選出一個 leeder,讓 Leader 來執行主從切換。

選舉 leeder 的過程其實是一個投票的過程,在投票開始前,肯定得有個「候選者」。

那誰來作為候選者呢?

哪個哨兵節點判斷主節點為「客觀下線」,這個哨兵節點就是候選者,所謂的候選者就是想當 Leader 的哨兵。

舉個例子,假設有三個哨兵。當哨兵 A 先判斷到主節點「主觀下線後」,就會給其他例項傳送 is-master-down-by-addr 命令。接著,其他哨兵會根據自己和主節點的網路連線情況,做出贊成投票或者拒絕投票的響應。

為什麼 Redis 要有哨兵機制?

當哨兵 A 收到贊成票數達到哨兵配置檔案中的 quorum 配置項設定的值後,就會將主節點標記為「客觀下線」,此時的哨兵 A 就是一個Leader 候選者。

候選者如何選舉成為 Leader?

候選者會向其他哨兵傳送命令,表明希望成為 Leader 來執行主從切換,並讓所有其他哨兵對它進行投票。

每個哨兵只有一次投票機會,如果用完後就不能參與投票了,可以投給自己或投給別人,但是隻有候選者才能把票投給自己。

那麼在投票過程中,任何一個「候選者」,要滿足兩個條件:

  • 第一,拿到半數以上的贊成票;
  • 第二,拿到的票數同時還需要大於等於哨兵配置檔案中的 quorum 值。

舉個例子,假設哨兵節點有 3 個,quorum 設定為 2,那麼任何一個想成為 Leader 的哨兵只要拿到 2 張贊成票,就可以選舉成功了。如果沒有滿足條件,就需要重新進行選舉。

這時候有的同學就會問了,如果某個時間點,剛好有兩個哨兵節點判斷到主節點為客觀下線,那這時不就有兩個候選者了?這時該如何決定誰是 Leader 呢?

每位候選者都會先給自己投一票,然後向其他哨兵發起投票請求。如果投票者先收到「候選者 A」的投票請求,就會先投票給它,如果投票者用完投票機會後,收到「候選者 B」的投票請求後,就會拒絕投票。這時,候選者 A 先滿足了上面的那兩個條件,所以「候選者 A」就會被選舉為 Leader。

為什麼哨兵節點至少要有 3 個?

如果哨兵叢集中只有 2 個哨兵節點,此時如果一個哨兵想要成功成為 Leader,必須獲得 2 票,而不是 1 票。

所以,如果哨兵叢集中有個哨兵掛掉了,那麼就只剩一個哨兵了,如果這個哨兵想要成為 Leader,這時票數就沒辦法達到 2 票,就無法成功成為 Leader,這時是無法進行主從節點切換的。

因此,通常我們至少會配置 3 個哨兵節點。這時,如果哨兵叢集中有個哨兵掛掉了,那麼還剩下兩個個哨兵,如果這個哨兵想要成為 Leader,這時還是有機會達到 2 票的,所以還是可以選舉成功的,不會導致無法進行主從節點切換。

當然,你要問,如果 3 個哨兵節點,掛了 2 個怎麼辦?這個時候得人為介入了,或者增加多一點哨兵節點。

再說一個問題,Redis 1 主 4 從,5 個哨兵 ,quorum 設定為 3,如果 2 個哨兵故障,當主節點當機時,哨兵能否判斷主節點“客觀下線”?主從能否自動切換?

  • 哨兵叢集可以判定主節點“客觀下線”。哨兵叢集還剩下 3 個哨兵,當一個哨兵判斷主節點“主觀下線”後,詢問另外 2 個哨兵後,有可能能拿到 3 張贊同票,這時就達到了 quorum 的值,因此,哨兵叢集可以判定主節點為“客觀下線”。

  • 哨兵叢集可以完成主從切換。當有個哨兵標記主節點為「客觀下線」後,就會進行選舉 Leader 的過程,因為此時哨兵叢集還剩下 3 個哨兵,那麼還是可以拿到半數以上(5/2+1=3)的票,而且也達到了 quorum 值,滿足了選舉 Leader 的兩個條件, 所以就能選舉成功,因此哨兵叢集可以完成主從切換。

如果 quorum 設定為 2 ,並且如果有 3 個哨兵故障的話。此時哨兵叢集還是可以判定主節點為“客觀下線”,但是哨兵不能完成主從切換了,大家可以自己推演下。

如果 quorum 設定為 3,並且如果有 3 個哨兵故障的話,哨兵叢集即不能判定主節點為“客觀下線”,也不能完成主從切換了。

可以看到,quorum 為 2 的時候,並且如果有 3 個哨兵故障的話,雖然可以判定主節點為“客觀下線”,但是不能完成主從切換,這樣感覺「判定主節點為客觀下線」這件事情白做了一樣,既然這樣,還不如不要做,quorum 為 3 的時候,就可以避免這種無用功。

所以,quorum 的值建議設定為哨兵個數的二分之一加1,例如 3 個哨兵就設定 2,5 個哨兵設定為 3,而且哨兵節點的數量應該是奇數

如何通知客戶端新主節點的資訊?

經過前面一系列的操作後,哨兵叢集終於完成了主從故障遷移,那麼新主節點的資訊要如何通知給客戶端呢?

這主要通過 Redis 的釋出者/訂閱者機制來實現的。每個哨兵節點提供釋出者/訂閱者機制,客戶端可以從哨兵訂閱訊息。

比如,客戶端訂閱了主從切換的事件,當哨兵把新主節點選擇出來後,就會發布新主節點的 IP 地址和埠資訊,這個時候客戶端就可以收到這條資訊,然後用這裡面的新主節點的 IP 地址和埠進行通訊了。

哨兵叢集是如何組成的?

前面提到了 Redis 的釋出者/訂閱者機制,那就不得不提一下哨兵叢集的組成方式,因為它也用到了這個技術。

在我第一次搭建哨兵叢集的時候,當時覺得很詫異。因為在配置哨兵的資訊時,竟然只需要填下面這幾個引數,設定主節點名字、主節點的 IP 地址和埠號以及 quorum 值。

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

不需要填其他哨兵節點的資訊,我就好奇它們是如何感知對方的,又是如何組成哨兵叢集的?

後面才瞭解到,哨兵節點之間是通過 Redis 的釋出者/訂閱者機制來相互發現的

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

在下圖中,哨兵 A 把自己的 IP 地址和埠的資訊釋出到__sentinel__:hello 頻道上,哨兵 B 和 C 訂閱了該頻道。那麼此時,哨兵 B 和 C 就可以從這個頻道直接獲取哨兵 A 的 IP 地址和埠號。然後,哨兵 B、C 可以和哨兵 A 建立網路連線。

為什麼 Redis 要有哨兵機制?

通過這個方式,哨兵 B 和 C 也可以建立網路連線,這樣一來,哨兵叢集就形成了。

哨兵叢集會對「從節點」的執行狀態進行監控,那哨兵叢集如何知道「從節點」的資訊?

主節點知道所有「從節點」的資訊,所以哨兵會向主節點傳送 INFO 命令來獲取所有「從節點」的資訊。

如下圖所示,哨兵 B 給主節點傳送 INFO 命令,主節點接受到這個命令後,就會把從節點列表返回給哨兵。接著,哨兵就可以根據從節點列表中的連線資訊,和每個從節點建立連線,並在這個連線上持續地對從節點進行監控。哨兵 A 和 C 可以通過相同的方法和從節點建立連線。

為什麼 Redis 要有哨兵機制?

正式通過 Redis 的釋出者/訂閱者機制,哨兵之間可以相互感知,然後組成叢集,同時,哨兵又通過 INFO 命令,在主節點裡獲得了所有從節點連線資訊,於是就能和從節點建立連線,並進行監控了。

總結

Redis 在 2.8 版本以後提供的哨兵(Sentinel)機制,它的作用是實現主從節點故障轉移。它會監測主節點是否存活,如果發現主節點掛了,它就會選舉一個從節點切換為主節點,並且把新主節點的相關資訊通知給從節點和客戶端。

哨兵一般是以叢集的方式部署,至少需要 3 個哨兵節點,哨兵叢集主要負責三件事情:監控、選主、通知

哨兵節點通過 Redis 的釋出者/訂閱者機制,哨兵之間可以相互感知,相互連線,然後組成哨兵叢集,同時哨兵又通過 INFO 命令,在主節點裡獲得了所有從節點連線資訊,於是就能和從節點建立連線,並進行監控了。

哨兵叢集會通過投票的方式判定主節點是否「客觀下線」,如果判定主節點為客觀下線,那麼就會從所有的「從節點」中選擇一個作為新主節點,選擇的規則有以下步驟:

  • 過濾掉已經離線的從節點;
  • 過濾掉歷史網路連線狀態不好的從節點;
  • 將剩下的從節點,進行三輪考察:優先順序、複製進度、ID 號。在每一輪考察過程中,如果找到了一個勝出的從節點,就將其作為新主節點。

選擇好從節點後,就需要從哨兵叢集選擇一個 leader 執行主從切換。選舉 leader 的過程,也是一個投票的過程,任何一個想成為 leader 的哨兵節點,要滿足兩個條件:

  • 第一,拿到半數以上的贊成票;
  • 第二,拿到的票數同時還需要大於等於哨兵配置檔案中的 quorum 值。

選舉完 leader 哨兵節點後,就執行主從切換。完成主從切換後,通過 Redis 的釋出者/訂閱者機制通知客戶端新主節點的 IP 地址和埠。

完!