1. 程式人生 > >Redis主從複製架構和Sentinel哨兵機制

Redis主從複製架構和Sentinel哨兵機制

Redis專題地址:https://www.cnblogs.com/hello-shf/category/1615909.html

SpringBoot讀原始碼系列:https://www.cnblogs.com/hello-shf/category/1456313.html

Elasticsearch系列:https://www.cnblogs.com/hello-shf/category/1550315.html

資料結構系列:https://www.cnblogs.com/hello-shf/category/1519192.html

一、redis主從複製原理

redis主從同步策略:slave剛加入叢集會觸發一次全量同步(全量複製)。全量同步之後,進行增量複製。slave優先是增量同步,如果增量同步失敗會嘗試從master節點進行全量複製。

全量複製:slave初始化階段

如上圖

  1. slave伺服器首先連線master節點,傳送SYNC命令。
  2. master節點收到SYNC命令開始指令BGSAVE(延遲寫,讀正常)命令生成RDB(快照)檔案,在此期間master的寫請求會被快取。
  3. RDB生成,master會將該RDB傳送給所有的slave。
  4. slave收到RDB檔案會拋棄之前的舊資料,然後載入新的RDB。
  5. master完成RDB傳送,會執行之前阻塞的寫命令,並將這些命令同步到slave節點。(增量複製)
  6. 以上步驟完成後master和slave開始正常工作。

值得注意的是,全量複製對主從都是非阻塞的,是非同步複製。


增量複製:master每次的寫命令會同步到slave,slave收到命令執行對應命令。

斷點續傳:在步驟3步驟中,master傳送給slave的檔案過程中,網路故障了,重連後,master僅會複製給slave缺少的部分(會記錄offset偏移量)。

master node 會在記憶體中維護一個 backlog,master 和 slave 都會儲存一個 replica offset 還有一個 master run id,offset 就是儲存在 backlog 中的。如果 master 和 slave 網路連線斷掉了,slave 會讓 master 從上次 replica offset 開始繼續複製,如果沒有找到對應的 offset,那麼就會執行一次 resynchronization。

主從複製中的記憶體淘汰策略:slave不會主動淘汰過期key,master處理掉的過期key,會向slave傳送一個del命令,同步淘汰的資料。

心跳資料:master 預設每隔 10秒 傳送一次 heartbeat,slave node 每隔 1秒 傳送一個 heartbeat。

注意,如果採用了主從架構,那麼建議必須開啟 master node 的持久化,不建議用 slave node 作為 master node 的資料熱備,因為那樣的話,如果你關掉 master 的持久化,可能在 master 宕機重啟的時候資料是空的,然後可能一經過複製, slave node 的資料也丟了。
另外,master 的各種備份方案,也需要做。萬一本地的所有檔案丟失了,從備份中挑選一份 rdb 去恢復 master,這樣才能確保啟動的時候,是有資料的,即使採用了高可用機制,slave node 可以自動接管 master node,但也可能 sentinel 還沒檢測到 master failure,master node 就自動重啟了,還是可能導致上面所有的 slave node 資料被清空。

二、哨兵機制

哨兵之前的主從架構是需要在各自的配置檔案中手動配置自己的master或者slave節點。(方便理解後面的Configuration傳播)
假如採用了redis的主從方案,那麼當master節點宕機,主備切換的過程是需要運維人員手動完成的,人工的故障轉移是十分的耗時而且對運維人員的要求是極高的。
所以就有了基於redis哨兵(Sentinel)機制搭建的高可用架構。

1,sentinel

中文哨兵。哨兵是 redis 叢集機構中非常重要的一個元件,主要有以下功能:

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

哨兵用於實現 redis 叢集的高可用,本身也是分散式的,作為一個哨兵叢集去執行,互相協同工作。

  • 故障轉移時,判斷一個 master node 是否宕機了,需要大部分的哨兵都同意才行,涉及到了分散式選舉的問題。
  • 即使部分哨兵節點掛掉了,哨兵叢集還是能正常工作的,因為如果一個作為高可用機制重要組成部分的故障轉移系統本身是單點的,那就很坑爹了。

2,核心知識

  • 哨兵至少需要 3 個例項,來保證自己的健壯性。
  • 哨兵 + redis 主從的部署架構,是不保證資料零丟失的,只能保證 redis 叢集的高可用性。
  • 對於哨兵 + redis 主從這種複雜的部署架構,儘量在測試環境和生產環境,都進行充足的測試和演練。

3,故障轉移

  • sdown 是主觀宕機,就一個哨兵如果自己覺得一個 master 宕機了,那麼就是主觀宕機
  • odown 是客觀宕機,如果 quorum 數量的哨兵都覺得一個 master 宕機了,那麼就是客觀宕機

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

三、slave->master 選舉演算法

如果一個 master 被認為 odown 了,而且 majority 數量的哨兵都允許主備切換,那麼某個哨兵就會執行主備切換操作,此時首先要選舉一個 slave 來,會考慮 slave 的一些資訊:

  • 跟 master 斷開連線的時長
  • slave 優先順序
  • 複製 offset
  • run id

如果一個 slave 跟 master 斷開連線的時間已經超過了 down-after-milliseconds 的 10 倍,外加 master 宕機的時長,那麼 slave 就被認為不適合選舉為 master。
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

接下來會對 slave 進行排序:

  • 按照 slave 優先順序進行排序,slave priority 越低,優先順序就越高。
  • 如果 slave priority 相同,那麼看 replica offset,哪個 slave 複製了越多的資料,offset 越靠後,優先順序就越高。
  • 如果上面兩個條件都相同,那麼選擇一個 run id 比較小的那個 slave。

quorum 和 majority:


每次一個哨兵要做主備切換,首先需要 quorum 數量的哨兵認為 odown,然後選舉出一個哨兵來做切換,這個哨兵還需要得到 majority 哨兵的授權,才能正式執行切換。
如果 quorum < majority,比如 5 個哨兵,majority 就是 3,quorum 設定為 2,那麼就 3 個哨兵授權就可以執行切換。
但是如果 quorum >= majority,那麼必須 quorum 數量的哨兵都授權,比如 5 個哨兵,quorum 是 5,那麼必須 5 個哨兵都同意授權,才能執行切換。


主備切換帶來的資料丟失問題


資料丟失的兩種情況:

  • 非同步複製導致的資料丟失

因為 master->slave 的複製是非同步的,所以可能有部分資料還沒複製到 slave,master 就宕機了,此時這部分資料就丟失了。

  • 腦裂導致的資料丟失

腦裂,也就是說,某個 master 所在機器突然脫離了正常的網路,跟其他 slave 機器不能連線,但是實際上 master 還執行著。此時哨兵可能就會認為 master 宕機了,然後開啟選舉,將其他 slave 切換成了 master。這個時候,叢集裡就會有兩個 master ,也就是所謂的腦裂。
此時雖然某個 slave 被切換成了 master,但是可能 client 還沒來得及切換到新的 master,還繼續向舊 master 寫資料。因此舊 master 再次恢復的時候,會被作為一個 slave 掛到新的 master 上去,自己的資料會清空,重新從新的 master 複製資料。而新的 master 並沒有後來 client 寫入的資料,因此,這部分資料也就丟失了。


解決方案:


進行如下配置:

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

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

  • 減少非同步複製資料的丟失

有了 min-slaves-max-lag 這個配置,就可以確保說,一旦 slave 複製資料和 ack 延時太長,就認為可能 master 宕機後損失的資料太多了,那麼就拒絕寫請求,這樣可以把 master 宕機時由於部分資料未同步到 slave 導致的資料丟失降低的可控範圍內。

  • 減少腦裂的資料丟失

如果一個 master 出現了腦裂,跟其他 slave 丟了連線,那麼上面兩個配置可以確保說,如果不能繼續給指定數量的 slave 傳送資料,而且 slave 超過 10 秒沒有給自己 ack 訊息,那麼就直接拒絕客戶端的寫請求。因此在腦裂場景下,最多就丟失 10 秒的資料。


四、哨兵叢集的自動發現機制


哨兵互相之間的發現,是通過 redis 的 pub/sub 系統實現的,每個哨兵都會往 __sentinel__:hello 這個 channel 裡傳送一個訊息,這時候所有其他哨兵都可以消費到這個訊息,並感知到其他的哨兵的存在。
每隔兩秒鐘,每個哨兵都會往自己監控的某個 master+slaves 對應的 __sentinel__:hello channel 裡傳送一個訊息,內容是自己的 host、ip 和 runid 還有對這個 master 的監控配置。
每個哨兵也會去監聽自己監控的每個 master+slaves 對應的 __sentinel__:hello channel,然後去感知到同樣在監聽這個 master+slaves 的其他哨兵的存在。
每個哨兵還會跟其他哨兵交換對 master 的監控配置,互相進行監控配置的同步。


五、configuration 傳播


哨兵完成切換之後,會在自己本地更新生成最新的 master 配置,然後同步給其他的哨兵,就是通過之前說的 pub/sub 訊息機制。
這裡之前的 version 號就很重要了,因為各種訊息都是通過一個 channel 去釋出和監聽的,所以一個哨兵完成一次新的切換之後,新的 master 配置是跟著新的 version 號的。其他的哨兵都是根據版本號的大小來更新自己的 master 配置的。


參考文獻:
  https://github.com/hello-shf/advanced-java
  https://www.cnblogs.com/daofaziran/p/10978628.html

  如有錯誤的地方還請留言指正。
  原創不易,轉載請註明原文地址:https://www.cnblogs.com/hello-shf/p/12072330.