引言
本文主要介紹 Redis 集群主節點故障的解決方案: 哨兵機制.
解決什么問題
Redis 集群中, master 主節點發生故障怎么辦?
哨兵(Sentinel)主要是為了解決在主從復制架構中出現宕機的情況,主要分為兩種情況:
1).從Redis宕機
這個相對而言比較簡單,在Redis中從庫重新啟動后會自動加入到主從架構中,自動完成同步數據。在Redis2.8版本后,主從斷線后恢復
的情況下實現增量復制。
2).主Redis宕機
這個相對而言就會復雜一些,需要以下2步才能完成
a. 在從數據庫中執行SLAVEOF NO ONE命令,斷開主從關系并且提升為主庫繼續服務
b. 第二步,將主庫重新啟動后,執行SLAVEOF命令,將其設置為其他庫的從庫,這時數據就能更新回來
由于這個手動完成恢復的過程其實是比較麻煩的并且容易出錯,所以Redis提供的哨兵(sentinel)的功能來解決.
實現目標
實現 redis 故障轉移的自動化。
自動發現,自動轉移。
不需要人工參與。
架構拓撲
Redis Sentinel 是一個分布式系統,為Redis提供高可用性解決方案。可以在一個架構中運行多個 Sentinel 進程(progress), 這些進程使用流言協議 (gossip protocols) 來接收關于主服務器是否下線的信息, 并使用投票協議(agreement protocols)來決定是否執行自動故 障遷移, 以及選擇哪個從服務器作為新的主服務器。
核心思想
Sentinel(哨兵)是Redis 的高可用性解決方案:由一個或多個Sentinel 實例 組成的Sentinel 系統可以監視任意多個主服務器,以及這些主服務器屬下的所有從服務器,并在被監視的主服務器進入下線狀態時,自動將下線主服務器屬下的某個從服務器升級為新的主服務器。
如圖所示
在Server1 掉線后:
升級Server2 為新的主服務器:
Redis 的 Sentinel 系統用于管理多個 Redis 服務器(instance) 該系統執行以下三個任務:
監控(Monitoring): Sentinel 會不斷地定期檢查你的主服務器和從服務器是否運作正常。
提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
自動故障遷移(Automaticfailover): 當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會將失效主服務器的其中 一個從服務器升級為新的主服務器, 并讓失效主服務器的其他從服務器改為復制新的主服務器; 當客 戶端試圖連接失效的主服務器時, 集群也會向客戶端返回新主服務器的地址, 使得集群可以使用新主 服務器代替失效服務器。
哨兵leader選舉算法
如果主節點被判定為客觀下線之后,就要選取一個哨兵節點來完成后面的故障轉移工作,選舉出一個leader的流程如下:
a)每個在線的哨兵節點都可以成為領導者,當它確認(比如哨兵3)主節點下線時,會向其它哨兵發is-master-down-by-addr
命令,征求判斷并要求將自己設置為領導者,由領導者處理故障轉移;
b)當其它哨兵收到此命令時,可以同意或者拒絕它成為領導者;
c)如果哨兵3發現自己在選舉的票數大于等于num(sentinels)/2+1
時,將成為領導者,如果沒有超過,繼續選舉…………
主觀下線:所謂主觀下線,就是單個sentinel認為某個服務下線(有可能是接收不到訂閱,之間的網絡不通等等原因)。
sentinel會以每秒一次的頻率向所有與其建立了命令連接的實例(master,從服務,其他sentinel)發ping命令,通過判斷ping回復是有效回復,還是無效回復來判斷實例時候在線(對該sentinel來說是“主觀在線”)。
sentinel配置文件中的down-after-milliseconds設置了判斷主觀下線的時間長度,如果實例在down-after-milliseconds毫秒內,返回的都是無效回復,那么sentinel回認為該實例已(主觀)下線,修改其flags狀態為SRI_S_DOWN。如果多個sentinel監視一個服務,有可能存在多個sentinel的down-after-milliseconds配置不同,這個在實際生產中要注意。
客觀下線:當主觀下線的節點是主節點時,此時該哨兵3節點會通過指令sentinel is-masterdown-by-addr尋求其它哨兵節點對主節點的判斷,如果其他的哨兵也認為主節點主觀線下了,則當認為主觀下線的票數超過了quorum(選舉)個數,此時哨兵節點則認為該主節點確實有問題,這樣就客觀下線了,大部分哨兵節點都同意下線操作,也就說是客觀下線:
哨兵至少需要3個實例,來保證自己的健壯性。哨兵+redis主從的部署架構,是不會保證數據零丟失的,只能保證redis集群的高可用性. 對于哨兵+redis主從這種復雜的部署架構,盡量在測試環境和生產環境,都進行充分的測試和演練。
自動故障轉移機制
在從節點(slave node) 中選擇新的主節點(master node)
sentinel狀態數據結構中保存了主服務的所有從服務信息,領頭sentinel按照如下的規則從從服務列表中挑選出新的主服務
- 過濾掉主觀下線的節點
- 選擇slave-priority最高的節點,如果由則返回沒有就繼續選擇
- 選擇出復制偏移量最大的系節點,因為復制便宜量越大則數據復制的越完整,如果由就返回了,沒有就繼續
- 選擇run_id最小的節點
更新主從狀態
通過slaveof no one命令,讓選出來的從節點成為主節點;并通過slaveof命令讓其他節點成為其從節點。
將已下線的主節點設置成新的主節點的從節點,當其回復正常時,復制新的主節點,變成新的主節點的從節點.
redis哨兵主備切換的數據丟失問題
兩種丟失情況:
異步復制
因為master->slave的復制是異步的,所以可能有部分數據還沒復制到slave,master就宕機了,這些數據就丟失了。
腦裂
腦裂,也就是說,某個master所在機器突然脫離了正常的網絡,跟其他slave機器不能連接,但是實際上master還運行著, 這個時候,集群中就會出現兩個master。
此時雖然某個slave被切換成了master,但是可能client還沒來得及切換到新的master,還繼續寫向舊master數據可能就會丟失。因此master在恢復的時候,會被作為一個slave掛到新的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秒的數據
總結
哨兵架構,幾乎可以做到了我們的要實現的高可用,但是哨兵的選舉還是需要時間的,而且中間會阻塞客戶端的請求,假如我們的選舉消耗了1秒(實際可能幾秒,高則幾十秒),就在這1秒的時候來了客戶端的請求,那個請求也是不可用的,并且我們的讀寫的節點實際還是單節點的,怎么辦? 使用 Redis集群架構:
也就是我們Redis的集群其實就是一個個小的主從結合在一起(官方建議小于1000個小主從),變成了我們的Redis集群,每個小主從也就是我們的Redis數據分片。
Kotlin 開發者社區
國內第一Kotlin 開發者社區公眾號,主要分享、交流 Kotlin 編程語言、Spring Boot、Android、React.js/Node.js、函數式編程、編程思想等相關主題。
越是喧囂的世界,越需要寧靜的思考。