redis 主從復制的作用中有這么一句話“主從復制是高可用的基石”,那什么是高可用呢?高可用就是減少系統不能提供的時間,也就是常聽到的以 6 個 9 為基準。實現高可用必不可少的就是哨兵和集群。

圖片來自 Pexels
本文主要圍繞如下幾個方面介紹哨兵機制:
- 什么是哨兵
- 哨兵的作用
- 如何配置哨兵
- 哨兵工作原理
- 總結
本文實現環境:
- centos 7.3
- redis 4.0
- redis 工作目錄 /usr/local/redis
- 在虛擬機進行模擬操作
什么是哨兵
先簡單說幾句我們在配置主從復制時有一種情況就是主節點宕機了,誰來提供服務呢?
當主節點宕機后主從復制就沒有存在的意義了,數據為王的時代沒有了數據何談什么高可用。

這個時候就橫空出世了一位老大哥名叫哨兵,老大哥說這個問題我來幫你們處理。
既然主節點 master 作為老大不領你們玩了。我就從你們四個中間再挑選出來一位老大,然后你們跟著他玩。
等不帶你們玩的那個老大回來后他的身份就失效了,就不再是你們的老大了。他只能跟著我挑選出來的老大玩。
上邊這段對話過程就是我們配置哨兵的意義到底在哪,跟誰玩就是誰給誰數據,知道了哨兵的作用我們就在繼續。
最后我們用專業術語來解釋一下什么是哨兵:
哨兵,英文名 Sentinel,是一個分布式系統,用于對主從結構中的每一臺服務器進行監控,當主節點出現故障后通過投票機制來挑選新的主節點,并且將所有的從節點連接到新的主節點上。
哨兵的作用
上文中我們談到的對話過程就是哨兵的作用之一:自動故障轉移。
談到作用肯定就是這個哨兵到底在工作中到底干了什么事情。我們先用比較干巴的概念描述一下,然后在下文的工作原理會一一談到。
哨兵的三個作用:
- 監控:監控誰?支持主從結構的工作一個是主節點一個是從節點,那肯定就是監控這倆個了。監控主節點和從節點是否正常運行;檢測主節點是否存活,主節點和從節點運行情況。
- 通知:哨兵檢測的服務器出現問題時,會向其他的哨兵發送通知,哨兵之間就相當于一個微信群,每個哨兵發現的問題都會發在這個群里。
- 自動轉移故障:當檢測到主節點宕機后,斷開與宕機主節點連接的所有從節點,在從節點中選取一個作為主節點,然后將其他的從節點連接到這個最新主節點的上。并且告知客戶端最新的服務器地址。
這里有一個注意點,哨兵也是一臺 Redis 服務器,只是不對外提供任何服務。配置哨兵時配置為單數。
那么為什么配置哨兵服務器的數量為單數呢?帶著這個疑問你會在下文看到你想要的答案。
如何配置哨兵
準備工作
我們開始配置哨兵,開啟八個客戶端,三個哨兵、一個主節點、倆個從節點、一個主節點客戶端、一個從節點客戶端。

sentinel.conf 配置解讀
哨兵使用的配置文件是 sentinel.conf,如下圖:

我們來對 sentinel.conf 配置信息進行解讀:

但是大多數都是注釋,這里給大家提供一個命令來過濾這些無用信息:
cat sentinel.conf | grep -v '#' | grep -v '^$'

①port 26379:對外服務端口號。
②dir /tmp:存儲哨兵的工作信息。
③sentinel monitor mymaster 127.0.0.1 6379 2:監控的是誰,名字可以自定義,后邊的 2 代表的是,如果有倆個哨兵判斷這個主節點掛了那這個主節點就掛了,通常設置為哨兵個數一半加一。
④sentinel down-after-milliseconds mymaster 30000:哨兵連接主節點多長時間沒有響應就代表掛了。后邊 30000 是毫秒,也就是 30 秒。
⑤sentinel parallel-syncs mymaster 1:這個配置項是指在故障轉移時,最多有多少個從節點對新的主節點進行同步。
這個值越小完成故障轉移的時間就越長,這個值越大就意味著越 多的從節點因為同步數據而不可用。
⑥sentinel failover-timeout mymaster 180000:在進行同步的過程中,多長時間完成算有效,系統默認值是 3 分鐘。
開始配置
使用命令 cat sentinel.conf | grep -v '#' | grep -v '^$' >
./data/sentinel-26379.conf 把 sentinel.conf 過濾后的信息移到 /usr/local/redis/conf 下。

然后打開 sentinel-26379.conf 修改信息存放目錄:

再快速的復制兩個哨兵配置文件,端口為 26380 和 26381:
sed 's/26379/26381/g' sentinel-26379.conf > sentinel-26381.conf

測試主從復制處于正常工作狀態,啟動三臺 redis 服務器,端口分別為 6379、6380、6381:

查看主節點信息,是有倆臺從節點在連接著,端口分別為 6380、6381。
這里有一個小小的點就是 lag 怎么一個是 1 一個是 0 呢?lag 是延遲時間,我這里是本地測試所以會出現 0 的情況,使用云服務器是很少出現的。
lag 的值為 0 和 1 都屬于正常。

測試主節點添加一個 hash 值,hset kaka name kaka:

分別從 slave1 和 slave2 獲取 kaka 的值,檢測主從復制是否正常運行。
經過測試我們的主從結構是正常運行的,如下圖:


啟動一個哨兵 redis-sentinel 26379-sentinel.conf:

連接 26379 哨兵,主要是最后一行,監控的主節點名為 mymaster,狀態正常,從節點有倆個,哨兵數量為 1 個。

再來查看一下 26379 的哨兵配置信息,這個時候已經改動了:

在啟動一個 26380 的哨兵,redis-sentinel 26380-sentinel.conf,這里注意一下最后一行多了一條信息,這個 id 就是我們 26379 配置文件新增的 id。

然后我們來到哨兵 26379 的客戶端,同樣也是新增的 26380 哨兵的 id:

這個時候我們再查看一下 26379 哨兵的配置文件,第一次查看配置文件是沒有配置 26380 哨兵的,第二次查看時配置了 26380 哨兵后添加的信息。

最后我們需要把哨兵客戶端 3 啟動起來,端口號為 26381。啟動起來之后,我們的配置信息和服務端的信息也會改動,添加哨兵 26380 有的信息,哨兵 26381 也會有。
直到這里我們對哨兵的配置就結束了,接下來我們把主節點 Master 給宕掉。

等待 30 秒后我們來到 26379 哨兵的客戶端,這里新增了一些信息,那么這些信息都做了什么呢?讓我們細細道來。

這里邊的信息我們先需要知道幾個:
①+sdown:這個信息后是指三個哨兵里邊有一個認為主節點宕機了。
②+odown:這個信息是指其他倆個哨兵去連接了一下主節點,發現確實是主節點宕機了,然后發起了一輪投票。這里使用的是 redis 4.0,版本之間這塊信息有點差異。
③+switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380:直到這里是哨兵發起投票的結果,推選端口為 6380 的 redis 為主節點。
④+slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380:這里就把端口為 6381 與 6379 和新的主節點 6380 做了一個連接。
⑤+sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380:最后一句是端口為 6379 的還是沒有上線,于是給踢下線。
當我們在重新把 6379 的 redis 服務器上線后,就可以看到哨兵服務端響應了倆句。一句是去除 6379 的下線。最后一句就是重連 6379 到新的主節點上。


這個時候主節點就是 6380 了,在 6380 的 redis 客戶端設置值,檢測主從復制是否正常工作。
在新的主節點 6380 添加 list 類型:

在 6379 和 6381 獲取這個值,至此,我們的哨兵模式就配置完成了。


哨兵工作原理
配置完哨兵后,就需要對其工作原理進行解析了,只有知道其工作流程,才能對哨兵有更好的理解。
本文講解原理沒有那么干巴!讓你可以把一篇技術文章當故事去看。
進入正題,哨兵作用是監控、通知、故障轉移。那么工作原理也是圍繞這三點來講的。
監控工作流程

監控工作流程如下:
①哨兵發送 info 指令,并且保存所有哨兵狀態,主節點和從節點的信息。
②主節點會記錄 redis 實例的信息,主節點記錄的信息跟哨兵記錄的信息看起來是一樣的,實際上還是有點區別。
③哨兵會根據在主節點拿到的從節點信息,給對應的從節點也發送 info 指令。
④接著哨兵 2 來了,同樣的也會給主節點發送 info 指令,并且建立 cmd 連接。
⑤這個時候哨兵 2 也會保存跟哨兵 1 一樣的信息,只不過是保存的哨兵信息是 2 個。
⑥這個時候為了每個哨兵的信息都一致它們之間建立了一個發布訂閱。為了哨兵之間的信息長期對稱它們之間也會互發 ping 命令。
⑦當再來一個哨兵 3 時,也會做同樣的事情,給主節點和從節點發送 info。并且跟哨兵 1 和哨兵 2 建立連接。
通知工作流程
sentinel 會給主從的所有節點發送命令獲取其狀態,并且會把信息發布到哨兵的訂閱里。

故障轉移原理

哨兵會一直給主節點發送 publish sentinel:hello,直到哨兵報出 sdown,這個詞這會是有不是有點熟悉了。沒錯就是我們上文中把主節點斷開后哨兵服務端報出的信息。
哨兵報出主節點 sdown 后還沒有完,哨兵還會往內網里發布消息說明這個主節點掛了。發送的指令是 sentinel
is-master-down-by-address-port。
其余的哨兵接收到指令后,主節點掛了嗎?讓我去看看到底掛沒掛。發送的信息也是 hello。
其余的哨兵也會發送他們收到的信息并且發送指令 sentinel
is-master-down-by-address-port 到自己的內網,確認一下第一個發送 sentinel
is-master-down-by-address-port 的哨兵說你說的對,這個家伙確實掛了。
當所有人都認為主節點掛了后就會修改其狀態為 odown。當一個哨兵認為主節點掛了標記的是 sdown,當半數哨兵都認為掛了其標記的狀態是 odown。這也就是配置哨兵為什么配置單數的原因。
對于一個哨兵認為主節點掛了稱之為主觀下線,半數哨兵認為主節點掛了稱之為客官下線。
一旦被認為主節點客官下線后,哨兵就會進行下一步操作:
這時哨兵已經檢測到問題所在了,那么到底是那個哨兵去負責推選新的主節點呢!不能是張三也去,李四也去,王五也去,這樣就亂套了、于是就需要在所有的哨兵里選出領頭的,那么是如何選的呢!請看下圖。
這個時候,五個 sentinel 就在一起開會了,所有的哨兵都在一個內網中,然后他們會做一件事情就是五個 sentinel 會同時發送指令 sentinel
is-master-down-by-address-port 并且攜帶上自己競選次數和 runid。

每個 sentinel 既是參選者也是投票者,每個 sentinel 都有一票,信封就代表自己的投票權。

當 sentinel1 和 sentinel4 同時把指令發送到群里準備競選時,sentinel2 這個時候就說我先接到誰的指令就把票投給誰。
假如 sentinel1 發的早,那么 sentinel2 的票就會投給 sentinel1。

按照這樣的規則一直發起投票直到有一個 sentinel 的票數為總 sentinel 數量的一半之多。
假設說是 sentinel1 的票數滿足總哨兵數量的一半之多后,sentinel1 就會當選。這個時候就進行到了下一個階段。

在上邊哨兵已經選出了 sentinel1 為代表去所有的從節點找出一個作為主節點。這個挑選主節點不是隨便拿一個是有一定的規則的。
先把不在線的干掉:

響應慢的干掉,sentinel 會給所有的 redis 發送信息,響應速度慢的就會被干掉。

與原主節點斷開時間最久的干掉,這里由于演示不夠用了,所有新增了一個 slave5,沒有任何意義哈!

以上三個點都判斷結束后還有 salve4 和 slave5,就會根據優先原則來進行篩選:
- 首先會根據優先級,如果優先級一樣在進行其他判斷。
- 判斷 offset 偏移量,判斷數據同步性,假如說 slave4 的 offset 為 90,slave5 偏移量為 100。那么哨兵就會認為 slave4 的網絡是不是有問題,于是就會選 slave5 為新的主節點。那如果說是 slave4 和 slave5 的 offset 相同呢!還有最后一個判斷。
- 最后一步就是判斷 runid 了,也就是職場中的論資排輩了,也就說根據 runid 的創建時間來判斷,時間早的上位。

選出新的主節點后就要對所有的節點發送指令了。

總結
關于哨兵的所有知識點就已經說完了,本文最重要的就是哨兵的工作原理了。
我們在簡單的梳理一下其工作原理:
- 首先進行監控,并且所有的哨兵同步信息。
- 哨兵向訂閱里邊發布信息。
- 故障轉移:哨兵發現主節點下線→哨兵開啟投票競選負責人→由負責人推選新的主節點→新的主節點斷開原主節點,并且其他的從節點連接新的主節點,原主節點上線后作為從節點連接。
以上就是筆者對哨兵的理解,如果錯誤歡迎指出,以便及時改正。