1. 程式人生 > >老司機帶你玩轉面試(4):Redis 高可用之哨兵模式

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

![](https://cdn.geekdigging.com/Interview/mianshi_header_1.jpg) ## 前文回顧 建議前面文章沒看過的同學先看下前面的文章: [「老司機帶你玩轉面試(1):快取中介軟體 Redis 基礎知識以及資料持久化」](https://www.geekdigging.com/2020/07/12/2878870968/) [「老司機帶你玩轉面試(2):Redis 過期策略以及快取雪崩、擊穿、穿透」](https://www.geekdigging.com/2020/07/12/1506238325/) [「老司機帶你玩轉面試(3):Redis 高可用之主從模式」](https://www.geekdigging.com/2020/07/15/3004256369/) ## 哨兵模式 前面介紹了 Redis 的主從模式,主從模式只能實現讀高可用,致命的弱點是寫無法高可用,一旦 master 節點掛了,整個叢集將無法寫入資料,這並不符合我們對 Redis 高可用叢集的期望。 那麼,是不是有一種方法,可以做到不僅僅讀高可用,寫一樣要高可用,當然有,這就是我們今天要介紹的哨兵模式。 哨兵模式可以理解成主從模式的一個升級版,主從模式 master 節點和 slave 節點是一開始就定好的,而在哨兵模式中, master 節點是可以轉移,一旦發現當前的 master 節點掛掉,通過選舉可以指定一個 slave 節點晉升成為 master ,保證在任何情況下,都有 master 節點可以支援寫入操作,也間接實現了寫高可用。 ### 簡介 哨兵模式可以看做是前面主從模式的一個升級版,主從模式沒有故障轉移, master 節點掛了就掛了,而哨兵模式就是為了解決這個問題而出現的。 * 叢集監控:負責監控 Redis master 和 slave 程序是否正常工作。 * 訊息通知:如果某個 Redis 例項有故障,那麼哨兵負責傳送訊息作為報警通知給管理員。 * 故障轉移:如果 master node 掛掉了,會自動轉移到 slave node 上。 * 配置中心:如果故障轉移發生了,通知 client 客戶端新的 master 地址。 ### 核心 假如我們現在有兩個哨兵例項,就長下面這樣: ```shell +----+ +----+ | 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 。 ```shell 2 個哨兵,majority=2 3 個哨兵,majority=2 4 個哨兵,majority=2 5 個哨兵,majority=3 6 個哨兵,majority=3 7 個哨兵,majority=4 ... ``` 可以看到,只有在奇數的時候,是可以最大化的利用 majority 數量。 經典的三哨兵模型下面這樣: ```shell +----+ | 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 的資料就這麼沒了。 資料丟失的解決方案有麼?沒有,因為這個問題是客觀存在的,我們解決不了這個問題,只能儘量的去減少這個問題帶來的損失,這時,可以使用下面這兩個配置: ```shell 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 了。 ## 參考 https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-sentinel.md https://blog.csdn.net/weixin_40663800/article/details/