Redis-哨兵機制
一、主從復制的問題
我們講了 Redis 復制的主要內容,但 Redis 復制有一個缺點,當主機 Master 宕機以后,我們需要人工解決切換,比如使用 slaveof no one
。實際上主從復制 并沒有實現高可用。
高可用側重備份機器, 利用集群中系統的冗余,當系統中某臺機器發生損壞的時候,其他后備的機器可以迅速的接替它來啟動服務
如下圖所示:
一旦主節點宕機,寫服務無法使用,就需要手動去切換,重新選取主節點,手動設置主從關系。
那么如何解決呢?
如果我們有一個監控程序能夠監控各個機器的狀態及時作出調整,將手動的操作變成自動的。 Sentinel 的出現就是為了解決這個問題
二、哨兵機制的原理實現
-
Redis Sentinel
一個分布式架構,其中包含若干個 Sentinel 節點和 Redis 數據節點,每個 Sentinel 節點會對數據節點和其余 Sentinel 節點進行監 控,當它發現節點不可達時,會對節點做下線標識。 -
如果被標識的是主節點,它還會和其他 Sentinel 節點進行“協商”,當大多數 Sentinel 節點都認為主節點不可達時,它們會選舉出一個 Sentinel 節點來完成自動故障轉移的工作,同時會將這個變化實時通知給 Redis 應用方。整個過程完全是自動的,不需要人工來介入,所以這套方案很有效地解決了 Redis 的高可用問題。
如下圖所示:
哨兵機制原理.png
基本的故障轉移流程:
-
主節點出現故障,此時兩個從節點與主節點失去連接,主從復制失敗。
主節點出現故障.png -
每個 Sentinel 節點通過定期監控發現主節點出現了故障
哨兵節點定時任務發現故障.png -
多個 Sentinel 節點對主節點的故障達成一致會選舉出其中一個節點作為領導者負責故障轉移。
哨兵監控通知.png - Sentinel 領導者節點執行了故障轉移,整個過程基本是跟我們手動調整一致的,只不過是自動化完成的。
- 故障轉移后整個 Redis Sentinel 的結構,重新選舉了新的主節點
Redis Sentinel 具有以下幾個功能:
- 監控:Sentinel 節點會定期檢測 Redis 數據節點、其余 Sentinel 節點是否可達
- 通知:Sentinel 節點會將故障轉移的結果通知給應用方
- 主節點故障轉移:實現從節點晉升為主節點并維護后續正確的主從關系
- 配置提供者:在 Redis Sentinel 結構中,客戶端在初始化的時候連接的是 Sentinel 節點集合 ,從中獲取主節點信息。
同時Redis Sentinel 包含了若個 Sentinel 節點,這樣做也帶來了兩個好處:
- 對于節點的故障判斷是由多個 Sentinel 節點共同完成,這樣可以有效地防止誤判。
-
Sentinel 節點集合是由若干個 Sentinel 節點組成的,這樣即使個別 Sentinel 節點不可用,整個 Sentinel 節點集合依然是健壯的。
但是 Sentinel 節點本身就是獨立的 Redis 節點,只不過它們有一些特殊,它們不存儲數據, 只支持部分命令。
哨兵機器.png - 通過
docker-compose
構建好哨兵跟主從的環境
Sentinel的核心配置
-
sentinel monitor mymaster 127.0.0.1 7000 2
監控的主節點的名字、IP 和端口,最后一個2的意思是有幾臺 Sentinel 發現有問題,就會發生故障轉移,例如 配置為2,代表至少有2個 Sentinel 節點認為主節點 不可達,那么這個不可達的判定才是客觀的。對于設置的越小,那么達到下線的條件越寬松,反之越嚴格。一般建議將其設置為 Sentinel 節點的一半加1(注意:最后的參數不得大于conut(sentinel)) -
sentinel down-after-millseconds mymaster 30000
這個是超時的時間(單位為毫秒)。打個比方,當你去 ping 一個機器的時候,多長時間后仍 ping 不通,那么就認為它是有問題 -
sentinel parallel-syncs mymaster 1
當 Sentinel 節點集合對主節點故障判定達成一致時, Sentinel 領導者節點會做故障轉移操作,選出新的主節點,原來的從節點會向新的主節點發起復制操 作, parallel-syncs 就是用來限制在一次故障轉移之后,每次向新的主節點發起復制操作的從節點個數,指出 Sentinel 屬于并發還是串行。1代表每次只能 復制一個,可以減輕 Master 的壓力;
parallel-syncs配置.png -
sentinel auth-pass <master-name> <password>
如果 Sentinel 監控的主節點配置了密碼,sentinel auth-pass 配置通過添加主節點的密碼,防止 Sentinel 節點對主節點無法監控。 -
sentinel failover-timeout mymaster 180000
表示故障轉移的時間。
Sentinel命令
sentinel支持的合法命令如下:
- SENTINEL masters 顯示被監控的所有master以及它們的狀態.
- SENTINEL master <master name> 顯示指定master的信息和狀態;
- SENTINEL slaves <master name> 顯示指定master的所有slave以及它們的狀態; 4. SENTINEL get-master-addr-by-name <master name> 返回指定master的ip和端口, 如果正在進行failover或者failover已經完成,將會顯示被提升為master的slave的ip和端口。
- SENTINEL failover <master name> 強制sentinel執行failover,并且不需要得到其他sentinel的同意。 但是failover后會將最新的配置發送給其他sentinel。
修改配置:
sentinel monitor test 127.0.0.1 6379 2
添加新的監聽
SENTINEL REMOVE test
放棄對某個master監聽
SENTINEL set failover-timeout mymaster 180000
設置配置選項
應用端調用
Master可能會因為某些情況宕機了,如果在客戶端是固定一個地址去訪問,肯定是不合理的,所以客戶端請求是請求哨兵,從哨兵獲取主機地址的信息,或者是 從機的信息。可以實現一個例子
- 隨機選擇一個哨兵連接,獲取主機、從機信息
- 模擬客戶端定時訪問,實現簡單輪訓效果,輪訓從節點
- 連接失敗重試訪問
Sentinel 實現原理
講完了 Sentinel 的代碼實現,很多人對 Sentinel 還不懂其原理。那么接下來我們就來看下 Sentinel 的實現原理,主要分為以下三個步驟。
- 檢測問題,主要講的是三個定時任務,這三個內部的執行任務可以保證出現問題馬上讓 Sentinel 知道。
- 發現問題,主要講的是主觀下線和客觀下線。當有一臺 Sentinel 機器發現問題時,它就會主觀對它主觀下線。 但是當多個 Sentinel 都發現有問題的時候,才會出現客觀下線。
- 找到解決問題的人,主要講的是領導者選舉,如何在 Sentinel 內部多臺節點做領導者選舉,選出一個領導者。
- 解決問題,主要講的是故障轉移,即如何進行故障轉移。
三個定時任務
首先要講的是內部 S`entinel 會執行以下三個定時任務。
- 每10秒每個 Sentinel 對 Master 和 Slave 執行一次 Info Replication 。
- 每2秒每個 Sentinel 通過 Master 節點的 channel 交換信息(pub/sub)。
- 每1秒每個 Sentinel 對其他 Sentinel 和 Redis 執行 ping 。
第一個定時任務,指的是 Redis Sentinel 可以對 Redis 節點做失敗判斷和故障轉移,在 Redis 內部有三個定時任務作為基礎,來 Info Replication 發現 Slave 節點,這個命令可以確定主從關系。
第二個定時任務,類似于發布訂閱, Sentinel 會對主從關系進行判定,通過 sentinel:hello 頻道交互。了解主從關系可以幫助更好的自動化操作 Redis 。然后 Sentinel 會告知系統消息給其它 Sentinel 節點,最終達到共識,同時 Sentinel 節點能夠互相感知到對方。 第三個定時任務,指的是對每個節點和其它 Sentinel 進行心跳檢測,它是失敗判定的依據。
主觀下線和客觀下線
我們先來回顧一下 Sentinel 的配置。
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
那么什么是主觀下線呢?
每個 Sentinel 節點對 Redis 節點失敗的“偏見”。之所以是偏見,只是因為某一臺機器30秒內沒有得到回復。
那么如何做到客觀下線呢?
這個時候需要所有 Sentinel 節點都發現它30秒內無回復,才會達到共識。
領導者選舉方式
- 每個做主觀下線的sentinel節點,會向其他的sentinel節點發送命令,要求將它設置成為領導者
- 收到命令sentinel節點,如果沒有同意通過其它節點發送的命令,那么就會同意請求,否則就會拒絕
- 如果sentinel節點發現自己票數超過半數,同時也超過了
sentinel monitor mymaster 127.0.0.1 6379 2
超過2個的時候,就會成為領導者 - 進行故障轉移操作
如何選擇“合適的”Slave 節點
Redis 內部其實是有一個優先級配置的,在配置文件中 slave-priority
,這個參數是 Salve 節點的優先級配置,如果存在則返回,如果不存在則繼續。 當上面這個優先級不滿足的時候, Redis 還會選擇復制偏移量最大的 Slave節點,如果存在則返回,如果不存在則繼續。之所以選擇偏移量最大,這是因為偏移 量越小,和 Master 的數據越不接近,現在 Master 掛掉了,說明這個偏移量小的機器數據也可能存在問題,這就是為什么要選偏移量最大的 Slave 的原因。 如果發現偏移量都一樣,這個時候 Redis 會默認選擇 runid 最小的節點。
生產環境中部署技巧
- Sentinel 節點不應該部署在一臺物理“機器”上。
這里特意強調物理機是因為一臺物理機做成了若干虛擬機或者現今比較流行的容器,它們雖然有不同的 ** IP ** 地址,但實際上它們都是同一臺物理機,同 一臺物理機意味著如果這臺機器有什么硬件故障,所有的虛擬機都會受到影響,為了實現 Sentinel 節點集合真正的高可用,請勿將 ** Sentinel **節點部署在 同一臺物理機器上。 - 部署至少三個且奇數個的 Sentinel 節點。
- 通過增加 Sentinel 節點的個數提高對于故障判定的準確性,因為領導者選舉需要至少一半加1個節點。 奇數個節點可以在滿足該條件的基礎上節省一個節點。
哨兵常見問題
哨兵集群在發現 master node 掛掉后會進行故障轉移,也就是啟動其中一個 slave node 為 master node 。在這過程中,可能會導致數據丟失的情況。
- 異步復制導致數據丟失
因為master->slave的復制是異步,所以可能有部分還沒來得及復制到slave就宕機了,此時這些部分數據就丟失了。 - 集群腦裂導致數據丟失
腦裂,也就是說,某個master所在機器突然脫離了正常的網絡,跟其它slave機器不能連接,但是實際上master還運行著。
造成的問題:
- 此時哨兵可能就會認為master宕機了,然后開始選舉,將其它 slave 切換成 master 。這時候集群里就會有2個 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、集群腦裂導致的數據丟失
集群腦裂因為 client 還沒來得及切換成新的 master ,還繼續寫向舊的 master 的數據可能就丟失了通過 min-slaves-to-write 確保必須是有多少個從 節點連接,并且延遲時間小于 min-slaves-max-lag 多少秒。
當然對于客戶端需要怎么做呢?
對于 client 來講,就需要做些處理,比如先將數據緩存到內存當中,然后過一段時間處理,或者連接失敗,接收到錯誤切換新的 master 處理。
redis日志參數說明
以下列出的是客戶端可以通過訂閱來獲得的頻道和信息的格式: 第一個英文單詞是頻道/事件的名字, 其余的是數據的格式。 注意, 當格式中包含 instance details 字樣時, 表示頻道所返回的信息中包含了以下用于識別目標實例的內容:
<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>
- @ 字符之后的內容用于指定主服務器, 這些內容是可選的, 它們僅在 @ 字符之前的內容指定的實例不是主服務器時使用。
- +reset-master <instance details>:主服務器已被重置。
- +slave <instance details>:一個新的從服務器已經被 Sentinel 識別并關聯。
- +failover-state-reconf-slaves <instance details>:故障轉移狀態切換到了 reconf-slaves 狀態。
- +failover-detected <instance details>:另一個 Sentinel 開始了一次故障轉移操作,或者一個從服務器轉換成了主服務器。
- +slave-reconf-sent <instance details>:領頭(leader)的 Sentinel 向實例發送了 SLAVEOF 命令,為實例設置新的主服務器。
- +slave-reconf-inprog <instance details>:實例正在將自己設置為指定主服務器的從服務器,但相應的同步過程仍未完成。
- +slave-reconf-done <instance details>:從服務器已經成功完成對新主服務器的同步。
- -dup-sentinel <instance details>:對給定主服務器進行監視的一個或多個 Sentinel 已經因為重復出現而被移除 —— 當 Sentinel 實例重啟的時 候,就會出現這種情況。
- +sentinel <instance details>:一個監視給定主服務器的新 Sentinel 已經被識別并添加。
- +sdown <instance details>:給定的實例現在處于主觀下線狀態。
- -sdown <instance details>:給定的實例已經不再處于主觀下線狀態。
- +odown <instance details>:給定的實例現在處于客觀下線狀態。
- -odown <instance details>:給定的實例已經不再處于客觀下線狀態。
- +new-epoch <instance details>:當前的紀元(epoch)已經被更新。
- +try-failover <instance details>:一個新的故障遷移操作正在執行中,等待被大多數 Sentinel 選中(waiting to be elected by the majority)。
- +elected-leader <instance details>:贏得指定紀元的選舉,可以進行故障遷移操作了。
- +failover-state-select-slave <instance details>:故障轉移操作現在處于 select-slave 狀態 —— Sentinel 正在尋找可以升級為主服務器的從 服務器。
- no-good-slave <instance details>:Sentinel 操作未能找到適合進行升級的從服務器。Sentinel 會在一段時間之后再次嘗試尋找合適的從服務器 來進行升級,又或者直接放棄執行故障轉移操作。
- selected-slave <instance details>:Sentinel 順利找到適合進行升級的從服務器。
- failover-state-send-slaveof-noone <instance details>:Sentinel 正在將指定的從服務器升級為主服務器,等待升級功能完成。
- failover-end-for-timeout <instance details>:故障轉移因為超時而中止,不過最終所有從服務器都會開始復制新的主服務器(slaves will eventually be configured to replicate with the new master anyway)。
- failover-end <instance details>:故障轉移操作順利完成。所有從服務器都開始復制新的主服務器了。
- +switch-master <master name> <oldip> <oldport> <newip> <newport>:配置變更,主服務器的 IP 和地址已經改變。 這是絕大多數外部用戶都關 心的信息。