­

老司機帶你玩轉面試(4):Redis 高可用之哨兵模式

前文回顧

建議前面文章沒看過的同學先看下前面的文章:

「老司機帶你玩轉面試(1):快取中間件 Redis 基礎知識以及數據持久化」

「老司機帶你玩轉面試(2):Redis 過期策略以及快取雪崩、擊穿、穿透」

「老司機帶你玩轉面試(3):Redis 高可用之主從模式」

哨兵模式

前面介紹了 Redis 的主從模式,主從模式只能實現讀高可用,致命的弱點是寫無法高可用,一旦 master 節點掛了,整個集群將無法寫入數據,這並不符合我們對 Redis 高可用集群的期望。

那麼,是不是有一種方法,可以做到不僅僅讀高可用,寫一樣要高可用,當然有,這就是我們今天要介紹的哨兵模式。

哨兵模式可以理解成主從模式的一個升級版,主從模式 master 節點和 slave 節點是一開始就定好的,而在哨兵模式中, master 節點是可以轉移,一旦發現當前的 master 節點掛掉,通過選舉可以指定一個 slave 節點晉陞成為 master ,保證在任何情況下,都有 master 節點可以支援寫入操作,也間接實現了寫高可用。

簡介

哨兵模式可以看做是前面主從模式的一個升級版,主從模式沒有故障轉移, master 節點掛了就掛了,而哨兵模式就是為了解決這個問題而出現的。

  • 集群監控:負責監控 Redis master 和 slave 進程是否正常工作。
  • 消息通知:如果某個 Redis 實例有故障,那麼哨兵負責發送消息作為報警通知給管理員。
  • 故障轉移:如果 master node 掛掉了,會自動轉移到 slave node 上。
  • 配置中心:如果故障轉移發生了,通知 client 客戶端新的 master 地址。

核心

假如我們現在有兩個哨兵實例,就長下面這樣:

+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

再了解兩個參數: quorum 、 majority

  • quorum: 表示認為 master 宕機的哨兵數量。
  • majority: 表示授權進行主從切換的最少的哨兵數量,而這個數字,需要大於一半。

在只有兩個節點的情況下,如果 master 宕機, s1 和 s2 中只要有 1 個哨兵認為 master 宕機了,就可以進行切換,同時 s1 和 s2 會選舉出一個哨兵來執行故障轉移。

這時,需要 majority,也就是大多數哨兵都是運行的。

所以此時,如果此時僅僅是 M1 進程宕機了,哨兵 s1 正常運行,那麼故障轉移是 OK 的。但是如果是整個 M1 和 S1 運行的機器宕機了,那麼哨兵只有 1 個,此時就沒有 majority 來允許執行故障轉移,雖然另外一台機器上還有一個 R1,但是故障轉移不會執行。

所以就有了以下這一條建議:

  • 哨兵至少需要 3 個實例,來保證自己的健壯性,並且實例數量最好是奇數。

因為在進行選舉的時候,需要超過一半的哨兵同意,也就是 majority 。

2 個哨兵,majority=2
3 個哨兵,majority=2
4 個哨兵,majority=2
5 個哨兵,majority=3
6 個哨兵,majority=3
7 個哨兵,majority=4
...

可以看到,只有在奇數的時候,是可以最大化的利用 majority 數量。

經典的三哨兵模型下面這樣:

       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

如果 M1 所在機器宕機了,那麼三個哨兵還剩下 2 個,S2 和 S3 可以一致認為 master 宕機了,然後選舉出一個來執行故障轉移,同時 3 個哨兵的 majority 是 2,所以還剩下的 2 個哨兵運行著,就可以允許執行故障轉移。

Redis 哨兵模式數據丟失問題

首先先說一個結論:

  • 哨兵 + Redis 主從的部署架構,是不保證數據零丟失的,只能保證 Redis 集群的高可用性。

然後我們再說兩種會導致數據丟失的情況:

第一種:是非同步複製可能會導致數據丟失

由於 master 向 salve 複製數據是非同步,有可能,有部分數據還沒有向 salve 複製,這時 master 宕機了,那麼這部分數據就丟失了。

第二種:腦裂導致的數據丟失

腦裂是指,由於網路波動或者其他因素影響, master 所在的機器突然間無法被其他哨兵梭訪問到,但是實際上這個 master 節點還在正常運行中。

此時哨兵會以為這個 master 節點已經宕機,開始進行新的 master 節點的選舉,將其他的 salve 節點切換成了 master 節點,這時集群中就會存在兩個 master 節點,也就是腦裂產生了。

這時雖然產生了新的 master 節點,但是客戶端可能還沒進行切換,還在像老的 master 寫數據,但是當老的 master 恢復訪問的時候,會被作為一個 salve 掛載到新的 master 節點上,自己的數據會被清空,重新從新的 master 複製數據,而在腦裂過程中寫入老的 master 的數據就這麼沒了。

數據丟失的解決方案有么?沒有,因為這個問題是客觀存在的,我們解決不了這個問題,只能盡量的去減少這個問題帶來的損失,這時,可以使用下面這兩個配置:

min-slaves-to-write 1
min-slaves-max-lag 10

這兩個配置的意思是:

兩個參數的意思:

  • 要求至少有 1 個 slave ,數據複製和同步的延遲不能超過 10 秒。
  • 如果說一旦所有的 slave ,數據複製和同步的延遲都超過了 10 秒鐘,那麼這個時候, master 就不會再接收任何請求了。

(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: 主觀宕機,就一個哨兵如果自己覺得一個 master 宕機了,那麼就是主觀宕機。
  • odown: 客觀宕機,如果 quorum 數量的哨兵都覺得一個 master 宕機了,那麼就是客觀宕機。

sdown 達成的條件很簡單,如果一個哨兵 ping 一個 master,超過了 is-master-down-after-milliseconds 指定的毫秒數之後,就主觀認為 master 宕機了;如果一個哨兵在指定時間內,收到了 quorum 數量的其它哨兵也認為那個 master 是 sdown 的,那麼就認為是 odown 了。

參考

//github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-sentinel.md

//blog.csdn.net/weixin_40663800/article/details/90316507