A. Redis哨兵機制原理淺析
上一篇文章Redis主從復制原理中簡要地說明了主從復制的一個基本原理,包含全量復制、復制積壓緩沖區與增量復制等內容,有興趣的同學可以先看下。
利用主從復制,可以實現讀寫分離、數據備份等功能。但如果主庫宕機後,需要運維人員手動地將一個從庫提升為新主庫,並將其他從庫slaveof新主庫,以此來實現故障恢復。
因此, 主從模式的一個缺點,就在於無法實現自動化地故障恢復 。Redis後來引入了哨兵機制,哨兵機制大大提升了系統的高可用性。
哨兵,就是站崗放哨的,時刻監控周圍的一舉一動,在第一時間發現敵情並發出及時的警報。
Redis中的哨兵(Sentinel), 則是一個特殊的Redis實例 ,不過它並不存儲數據。也就是說,哨兵在啟動時,不會去載入RDB文件。
關於Redis的持久化,可以參考我的另外一篇文章 談談Redis的持久化——AOF日誌與RDB快照
上圖就是一個典型的哨兵架構,由數據節點與哨兵節點構成,通常會部署多個哨兵節點。
哨兵主要具有三個作用, 監控、選主與通知 。
監控:哨兵會利用心跳機制,周期性不斷地檢測主庫與從庫的存活性
選主:哨兵檢測到主庫宕機後,選擇一個從庫將之切換為新主庫
通知:哨兵會將新主庫的地址通知到所有從庫,使得所有從庫與舊主庫slaveof新主庫,也會將新主庫的地址通知到客戶端上
我會在下文詳細講一下監控與選主的過程
哨兵系統是通過3個定時任務,來完成對主庫、從庫與哨兵之間的探活。
首先我們會在配置文件中配置主庫地址,這樣哨兵在啟動後,會以 每隔10秒 的頻率向主庫發送info命令,從而獲得當前的主從拓撲關系,這樣就拿到了所有從庫的地址。
接著 每隔2秒 ,會使用pub/sub(發布訂閱)機制,在主庫上的 sentinel :hello的頻道上發布消息,消息內容包括哨兵自己的ip、port、runid與主庫的配置。
每個哨兵都會訂閱該頻道,在該頻道上發布與消費消息,從而實現哨兵之間的互相感知。
利用啟動配置與info命令可以獲取到主從庫地址,利用發布訂閱可以感知到其餘的哨兵節點。
在此基礎上,哨兵會 每隔1秒 向主庫、從庫與其他哨兵節點發送PING命令,因此來進行互相探活。
當某個哨兵在 **down-after-milliseconds(默認是30秒) **配置的連續時間內,仍然沒有收到主庫的正確響應,則當前哨兵會認為主庫 主觀下線 ,並將其標記為sdown(subjective down)
為了避免當前哨兵對主庫的誤判,因此這個時候還需要參考其他哨兵的意見。
接著當前哨兵會向其他哨兵發送 sentinel is-master-down-by-addr 命令, 如果有半數以上(由quorum參數決定)的哨兵認為主庫確實處於主觀下線狀態,則當前哨兵認為主庫客觀下線 ,標記為odown(objective down)
一旦某個主庫被認定為客觀下線時,這個時候需要進行哨兵選舉,選舉出一個領導者哨兵,來完成主從切換的過程。
哨兵A在向其他哨兵發送 sentinel is-master-down-by-addr 命令時,同時要求其他哨兵同意將其設置為Leader,也就是想獲得其他哨兵的投票。
在每一輪選舉中,每個哨兵僅有一票。投票遵循先來先到的原則,如果某個哨兵沒有投給別人,就會投給哨兵A。
首先獲得半數以上投票的哨兵,將被選舉稱為Leader。
這里的哨兵選舉,採用的是Raft演算法。這里不對Raft做詳細的探討,有興趣的同學,可以參考我的另外一篇文章 22張圖,帶你入門分布式一致性演算法Raft
該文章採用大量的圖例,相信你可以從中學習到全新的知識,從而打開分布式一致性演算法的大門,大夥們記得等我搞完Paxos與Zab。
過半投票機制也常用於很多演算法中,例如RedLock,在半數以上的節點上加鎖成功,才代表申請到了分布式鎖,具體可參考這篇文章的最後 我用了上萬字,走了一遍Redis實現分布式鎖的坎坷之路,從單機到主從再到多實例,原來會發生這么多的問題
在Zookeeper選舉中,同樣也用到了過半投票機制,在這篇文章中 面試官:能給我畫個Zookeeper選舉的圖嗎? 我從源碼角度分析了Zookeeper選舉的過程。
在選舉到領導者哨兵後,將由該哨兵完成故障恢復工作。
故障恢復分為以下兩步:
詳細說一下第一步,挑選是有條件的。首先要過濾出不健康的節點,再按某種規則排序,最後取第一個從庫,我們直接從源碼入手:
因此,以下從庫會被過濾出:
剩下的節點,就是健康的節點,此時再執行一次快速排序,排序的規則如下:
本文算是Redis哨兵的一個入門文章,主要講了哨兵的作用,例如監控、選主和通知。
在Redis讀寫分離的情況下,使用哨兵可以很輕松地做到故障恢復,提升了整體的可用性。
但哨兵無法解決Redis單機寫的瓶頸,這就需要引入集群模式,相應的文章也被列為明年的寫作計劃中。
</article>
B. Redis(7):哨兵機制
哨兵(sentinel),是redis集群架構中非常重要的一個組件,它主要的功能和特性如下:
(1)集群監控,負責監控redis master和slave進程是否正常工作。
(2)消息通知,如果某個redis實例有故障,那麼哨兵負責發送消息作為報警通知給管理員。
(3)故障轉移,如果master node掛掉了,會自動轉移到slave node上。
(4)配置中心,如果故障轉移發生了,通知client客戶端新的master地址。
(5)哨兵本身也是分布式的,哨兵以集群的方式來相互協作。
(6)要進行故障轉移時會涉及到分布式選舉,當大部分哨兵節點都同意時才能進行。
(7)當部分哨兵節點不能正常工作時,哨兵集群還是能夠正常工作。
說到這個特點,我們就要說到兩個參數,第一個是quorum,quorum是我們可以給哨兵集群配置的一個參數,它的作用是:當master節點宕機的時候需要有至少quorum個哨兵節點認為mater宕機才能進行後續的操作,比如主備切換、故障轉移,這個時候還需要一個參數majority來判斷是否可以進行故障轉移,需要有大多數哨兵節點正常工作來同同意這次故障轉移,即正常工作的哨兵節點數要大於majority,majority有一個通用的規則:2個哨兵節點的majority為2,3個哨兵節點的majority為2,4個哨兵節點的majority為2,5個哨兵節點的majority為3,以此類推。如果一個master節點宕機了,同時他的哨兵進程同時掛掉了,那麼現在正常運行的哨兵節點數目為1,即使你把quorum設置為1,滿足了quorum的條件,但是是不能滿足正常運行的哨兵節點大於majority,也就是2,所以是不能保證能夠進行故障轉移的。
第一種情況: maser節點和slave節點之間的數據傳輸不是同步的,是非同步的,當客戶端往master中寫數據的時,master非同步的發送給slave節點,這當中會存在一定的延遲,當master節點掛掉的時候是不能保證這一小部分數據會及時的發送給slave節點。
第二種情況:當master節點與slave節點間發生網路故障,導致無法與slave節點通訊,產生網路分區。這時,老的master節點並沒有視為宕機,仍然在不停的寫數據。此時這個slave節點如果被選舉為新的master節點,這樣就會發生「集群腦裂」,客戶端程序還是會源源不斷地將數據寫入老的master節點,新的master節點卻沒有接收到新的數據,當網路恢復正常,老的master節點降為slave節點的時候,會立即執行全量的主從復制,那麼故障之後的數據就會丟失。
現在我們有一主兩從的這樣一個redis 集群,每個redis節點有一個哨兵,我們設置quorum為2,那麼當master節點宕機的話,此時三個哨兵還剩下兩個,剩下的兩個slave節點一致認為master宕機,此時三個哨兵節點的majority為2,剩下還有2個正常運行的哨兵節點,那麼就可以選舉出新的master節點進行故障轉移。
非同步復制數據丟失,集群腦裂數據丟失的問題我們可以通過設置兩個參數來減少數據丟失:
min-slaves-to-write 1
min-slaves-max-lag 10
上面兩個參數表達的意思為:要求至少有1個slave,數據復制和同步的延遲不能超過10秒。也就是說,如果所有的slave節點都落後於master節點10秒鍾的數據,那麼master節點就不會再接受任何請求了。
(1)減少非同步復制的數據丟失
有了min-slaves-max-lag這個配置,一旦slave復制數據和ack延時太長,就認為可能master宕機後損失的數據太多了,那麼就拒絕寫請求,這樣可以把master宕機時由於部分數據未同步到slave導致的數據丟失降低的可控范圍內,最多也就丟失10秒的數據。
(2)減少腦裂的數據丟失
如果一個master出現了腦裂,跟其他slave丟了連接,那麼上面兩個配置可以確保說,如果不能繼續給指定數量的slave發送數據,而且slave超過10秒沒有給自己ack消息,那麼就直接拒絕客戶端的寫請求。這樣腦裂後的舊master就不會接受client的新數據,也就避免了數據丟失。上面的配置就確保了,如果跟任何一個slave丟了連接,在10秒後發現沒有slave給自己ack,那麼就拒絕新的寫請求。因此在腦裂場景下,最多就丟失10秒的數據。
(3)當master不接受請求之後該如何處理
當master不接受寫請求之後,我們需要給client做降級,讓它直接把數據寫到本地磁碟里,讓client自己處理這些數據,做相應的降級和限流,減慢數據湧入的速度。或者將數據寫入一個消息隊列,每隔一段時間取一次,嘗試將數據重新寫入到redis中。
什麼是sdown,sdown又稱為主觀宕機,即一個哨兵覺得master節點宕機,這就是sdown。odown又稱為客觀宕機,即現在有quorum個哨兵都認為master宕機了。判斷sdown的條件就是:一個哨兵ping一個master,超過了is-master-down-after-milliseconds參數指定的毫秒數之後,就主觀認為master宕機。sdown轉換到odown的條件就是:如果一個哨兵在指定時間內收到了超過quorum個哨兵的認為該master節點宕機的信息,就轉換成了odown。
哨兵互相之間的發現,是通過redis的pub/sub系統實現的,每個哨兵都會往__sentinel__:hello這個channel里發送一個消息,這時候所有其他哨兵都可以消費到這個消息,並感知到其他的哨兵的存在。每隔兩秒鍾,每個哨兵都會往自己監控的某個master+slaves對應的__sentinel__:hello channel里發送一個消息,內容是自己的host、ip和runid還有對這個master的監控配置。每個哨兵也會去監聽自己監控的每個master+slaves對應的__sentinel__:hello channel,然後去感知到同樣在監聽這個master+slaves的其他哨兵的存在。每個哨兵還會跟其他哨兵交換對master的監控配置,互相進行監控配置的同步。
哨兵會負責自動糾正slave的一些配置,比如slave如果要成為潛在的master候選人,哨兵會確保slave在復制現有master的數據; 如果slave連接到了一個錯誤的master上,比如故障轉移之後,那麼哨兵會確保它們連接到正確的master上。
如果一個master被認為odown了,而且majority哨兵都允許了主備切換,那麼某個哨兵就會執行主備切換操作,此時首先要選舉一個slave,會考慮slave的一些信息:
a.跟master斷開連接的時長
b.slave優先順序
c.復制offset
d.run id
如果一個slave跟master斷開連接已經超過了down-after-milliseconds的10倍(加上master宕機的時長),那麼slave就被認為不適合選舉為master。計算公式為:(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state。接下來會對slave進行排序:
a.按照slave優先順序進行排序,slave priority越低,優先順序就越高
b.如果slave priority相同,那麼看replica offset,哪個slave復制了越多的數據,offset越靠後,優先順序就越高
c.如果上面兩個條件都相同,那麼選擇一個run id比較小的那個slave
每次一個哨兵要做主備切換,首先需要quorum數量的哨兵認為odown,然後選舉出一個哨兵來做切換,這個哨兵還得得到majority哨兵的授權,才能正式執行切換。如果quorum < majority,比如5個哨兵,majority就是3,quorum設置為2,那麼就3個哨兵授權就可以執行切換,但是如果quorum >= majority,那麼必須quorum數量的哨兵都授權,比如5個哨兵,quorum是5,那麼必須5個哨兵都同意授權,才能執行切換。
哨兵會對一套redis master+slave進行監控,有相應的監控的配置,執行切換的那個哨兵,會從要切換到的新master(salve->master)那裡得到一個configuration epoch,這就是一個版本號,每次切換的version號都必須是唯一的。如果第一個選舉出的哨兵切換失敗了,那麼其他哨兵,會等待failover-timeout時間,然後接替繼續執行切換,此時會重新獲取一個新的configuration epoch,作為新的版本號。
哨兵完成切換之後,會在自己本地更新生成最新的master配置,然後同步給其他的哨兵,就是通過之前說的pub/sub消息機制。這里之前的版本號就很重要了,因為各種消息都是通過一個channel去發布和監聽的,所以一個哨兵完成一次新的切換之後,新的master配置是跟著新的版本號,其他的哨兵都是根據版本號的大小來更新自己的master配置的。
C. redis主從和哨兵
主從復制:主節點負責寫數據,從節點負責讀數據,主節點定期把數據同步到從節點保證數據的一致性
a,配置主從復制方式一、新增redis6380.conf, 加入 slaveof 192.168.152.128 6379, 在6379啟動完後再啟6380,完成配置;
b,配置主從復制方式二、redis-server --slaveof 192.168.152.128 6379 臨時生效
c,查看狀態:info replication
d,斷開主從復制:在slave節點,執行6380:>slaveof no one
e,斷開後再變成主從復制:6380:> slaveof 192.168.152.128 6379
f,數據較重要的節點,主從復制時使用密碼驗證: requirepass
e, 從節點建議用只讀模式slave-read-only=yes, 若從節點修改數據,主從數據不一致
h,傳輸延遲:主從一般部署在不同機器上,復制時存在網路延時問題,redis提供repl-disable-tcp-nodelay參數決定是否關閉TCP_NODELAY,默認為關閉
參數關閉時:無論大小都會及時發布到從節點,占帶寬,適用於主從網路好的場景,
參數啟用時:主節點合並所有數據成TCP包節省帶寬,默認為40毫秒發一次,取決於內核,主從的同步延遲40毫秒,適用於網路環境復雜或帶寬緊張,如跨機房
a)一主一從:用於主節點故障轉移從節點,當主節點的「寫」命令並發高且需要持久化,可以只在從節點開啟AOF(主節點不需要),這樣即保證了數據的安全性,也避免持久化對主節點的影響
b)一主多從:針對「讀」較多的場景,「讀」由多個從節點來分擔,但節點越多,主節點同步到多節點的次數也越多,影響帶寬,也加重主節點的穩定
c)樹狀主從:一主多從的缺點(主節點推送次數多壓力大)可用些方案解決,主節點只推送一次數據到從節點B,再由從節點B推送到C,減輕主節點推送的壓力。
redis 2.8版本以上使用psync命令完成同步,過程分「全量」與「部分」復制
全量復制:一般用於初次復制場景(第一次建立SLAVE後全量)
部分復制:網路出現問題,從節點再次連接主節點時,主節點補發缺少的數據,每次數據增量同步
心跳:主從有長連接心跳,主節點默認每10S向從節點發ping命令,repl-ping-slave-period控制發送頻率
a)主從復制,若主節點出現問題,則不能提供服務,需要人工修改配置將從變主
b)主從復制主節點的寫能力單機,能力有限
c)單機節點的存儲能力也有限
a)主節點(master)故障,從節點slave-1端執行 slaveof no one後變成新主節點;
b)其它的節點成為新主節點的從節點,並從新節點復制數據;
c)需要人工干預,無法實現高可用。
1. 為什麼要有哨兵機制?
原理:當主節點出現故障時,由Redis Sentinel自動完成故障發現和轉移,並通知應用方,實現高可用性。
其實整個過程只需要一個哨兵節點來完成,首先使用Raft演算法(選舉演算法)實現選舉機制,選出一個哨兵節點來完成轉移和通知
任務1:每個哨兵節點每10秒會向主節點和從節點發送info命令獲取最拓撲結構圖,哨兵配置時只要配置對主節點的監控即可,通過向主節點發送info,獲取從節點的信息,並當有新的從節點加入時可以馬上感知到
任務2:每個哨兵節點每隔2秒會向redis數據節點的指定頻道上發送該哨兵節點對於主節點的判斷以及當前哨兵節點的信息,同時每個哨兵節點也會訂閱該頻道,來了解其它哨兵節點的信息及對主節點的判斷,其實就是通過消息publish和subscribe來完成的
任務3:每隔1秒每個哨兵會向主節點、從節點及其餘哨兵節點發送一次ping命令做一次心跳檢測,這個也是哨兵用來判斷節點是否正常的重要依據
客觀下線:當主觀下線的節點是主節點時,此時該哨兵3節點會通過指令sentinel is-masterdown-by-addr尋求其它哨兵節點對主節點的判斷,當超過quorum(選舉)個數,此時哨兵節點則認為該主節點確實有問題,這樣就客觀下線了,大部分哨兵節點都同意下線操作,也就說是客觀下線
a)每個在線的哨兵節點都可以成為領導者,當它確認(比如哨兵3)主節點下線時,會向其它哨兵發is-master-down-by-addr命令,徵求判斷並要求將自己設置為領導者,由領導者處理故障轉移;
b)當其它哨兵收到此命令時,可以同意或者拒絕它成為領導者;
c)如果哨兵3發現自己在選舉的票數大於等於num(sentinels)/2+1時,將成為領導者,如果沒有超過,繼續選舉…………
a)由Sentinel節點定期監控發現主節點是否出現了故障
sentinel會向master發送心跳PING來確認master是否存活,如果master在「一定時間范圍」內不回應PONG 或者是回復了一個錯誤消息,那麼這個sentinel會主觀地(單方面地)認為這個master已經不可用了
b) 當主節點出現故障,此時3個Sentinel節點共同選舉了Sentinel3節點為領導,負載處理主節點的故障轉移
c) 由Sentinel3領導者節點執行故障轉移,過程和主從復制一樣,但是自動執行
流程:
1. 將slave-1脫離原從節點,升級主節點,
d) 故障轉移後的redis sentinel的拓撲結構圖
a) 過濾掉不健康的(下線或斷線),沒有回復過哨兵ping響應的從節點
b) 選擇salve-priority從節點優先順序最高(redis.conf)的
c) 選擇復制偏移量最大,指復制最完整的從節點
以3個Sentinel節點、2個從節點、1個主節點為例進行安裝部署
1. 前提: 先搭好一主兩從redis的主從復制,和之前的主從復制搭建一樣,搭建方式如下:
A)主節點6379節點(/usr/local/bin/conf/redis6379.conf):
修改 requirepass 12345678,注釋掉#bind 127.0.0.1
B) 從節點redis6380.conf和redis6381.conf: 配置都一樣
修改 requirepass 12345678 ,注釋掉#bind 127.0.0.1,
加上訪問主節點的密碼masterauth 12345678 ,加上slaveof 192.168.152.128 6379
2. redis sentinel哨兵機制核心配置 (也是3個節點):
將三個文件的埠改成: 26379 26380 26381
然後:sentinel monitor mymaster 192.168.152.128 6379 2 //監聽主節點6379
三個配置除埠外,其它一樣。
3. 哨兵其它的配置 :只要修改每個sentinel.conf的這段配置即可:
sentinel monitor mymaster 192.168.152.128 6379 2
//監控主節點的IP地址埠,sentinel監控的master的名字叫做mymaster,2代表,當集群中有2個sentinel認為master死了時,才能真正認為該master已經不可用了
sentinel auth-pass mymaster 12345678 //sentinel連主節點的密碼
sentinel config-epoch mymaster 2 //故障轉移時最多可以有2從節點同時對新主節點進行數據同步
sentinel leader-epoch mymaster 2
sentinel failover-timeout mymasterA **180000 **//故障轉移超時時間180s,
a,如果轉移超時失敗,下次轉移時時間為之前的2倍;
b,從節點變主節點時,從節點執行slaveof no one命令一直失敗的話,當時間超過 180S 時,則故障轉移失敗
c,從節點復制新主節點時間超過 180S 轉移失敗
sentinel down-after-milliseconds mymasterA 300000 //sentinel節點定期向主節點ping命令,當超過了 300S 時間後沒有回復,可能就認定為此主節點出現故障了……
sentinel parallel-syncs mymasterA 1 //故障轉移後, 1 代表每個從節點按順序排隊一個一個復制主節點數據,如果為3,指3個從節點同時並發復制主節點數據,不會影響阻塞,但存在網路和IO開銷
4. 啟動redis服務和sentinel服務:
a)先把之前安裝的redis裡面的標綠色的文件都拷貝到 usr/local/bin目錄下,然後再再bin目錄下新建一個conf文件夾存放配置好的redis主從配置文件和哨兵配置文件
b)啟動主從復制服務,先啟動主再啟動從
主:./redis-server conf/redis6379.conf &
從:
./redis-server conf/redis6380.conf &
./redis-server conf/redis6381.conf &
c)啟動sentinel服務:
./redis-sentinel conf/sentinel_26381.conf &
到此服務全部啟動完畢
連接到6379的redis的服務,可看到6379就是主節點,他有6380和6381兩個從節點
5. 測試: kill -9 6379 殺掉6379的redis服務
可以看到殺掉6379以後6380變為了主節點,6381變為了6380的從節點
重新啟動6379以後變為6380的從節點
看日誌是分配6380 是6381的主節點,當6379服務再啟動時,已變成從節點
假設6380升級為主節點:進入6380>info replication 可以看到role:master
打開sentinel_26379.conf等三個配置,sentinel monitor mymaster 192.168.152.128 6380 2
打開redis6379.conf等三個配置, slaveof 192.168.152.128 6380,也變成了6380
注意:生產環境建議讓redis Sentinel部署到不同的物理機上。
a,sentinel節點應部署在多台物理機(線上環境)
b,至少三個且奇數個sentinel節點
c,通過以上我們知道,3個sentinel可同時監控一個主節點或多個主節點
sentinel參考資料:
redis sentinel的機制與用法一: https://segmentfault.com/a/1190000002680804
redis sentinel的機制與用法二: https://segmentfault.com/a/1190000002685515