天天看點

Redis Sentinel實作的機制與原理詳解

序言

Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案。實際上這意味着你可以使用Sentinel模式建立一個可以不用人為幹預而應對各種故障的Redis部署。

它的主要功能有以下幾點

  • 監控:Sentinel不斷的檢查master和slave是否正常的運作。
  • 通知:如果發現某個redis節點運作出現問題,可以通過API通知系統管理者和其他的應用程式。
  • 自動故障轉移:能夠進行自動切換。當一個master節點不可用時,能夠選舉出master的多個slave中的一個來作為新的master,其它的slave節點會将它所追随的master的位址改為被提升為master的slave的新位址。
  • 配置提供者:哨兵作為Redis用戶端發現的權威來源:用戶端連接配接到哨兵請求目前可靠的master的位址。如果發生故障,哨兵将報告新位址。

sentinel的分布式特性

很顯然,隻使用單個sentinel程序來監控redis叢集是不可靠的,當sentinel程序宕掉後(sentinel本身也有單點問題,single-point-of-failure)整個叢集系統将無法按照預期的方式運作。是以有必要将sentinel叢集,這樣有幾個好處:

  • 即使有一些sentinel程序宕掉了,依然可以進行redis叢集的主備切換;
  • 如果隻有一個sentinel程序,如果這個程序運作出錯,或者是網絡堵塞,那麼将無法實作redis叢集的主備切換(單點問題);
  • 如果有多個sentinel,redis的用戶端可以随意地連接配接任意一個sentinel來獲得關于redis叢集中的資訊。

關于sentinel的穩定版本

目前的哨兵版本是sentinel 2。它是基于最初哨兵的實作,使用更健壯的和更簡單的預算算法(在這個文檔裡有解釋)重寫的。

Redis2.8和Redis3.0附帶穩定的哨兵版本。他們是Redis的兩個最新穩定版本。

在不穩定版本的分支上執行新的改進,且有時一些新特性一旦被認為是穩定的就會被移植到Redis2.8和Redis3.0分支中。

Redis2.6附帶Redis sentinel 1,它是棄用的不建議使用。

運作sentinel

運作Sentinel有兩種方式,如下:

redis-sentinel /path/to/sentinel.conf      
redis-server /path/to/sentinel.conf --sentinel      

兩種方式效果都是一樣的。

然而在啟動哨兵時必須使用一個配置檔案,因為這個配置檔案将用于系統儲存目前狀态和在重新開機時重新加載。哨兵會在沒有指定配置檔案或指定的配置檔案不可寫的時候拒絕啟動。

Redis 哨兵預設監聽26379 TCP端口,是以為了哨兵的正常工作,你的26379端口必須開放接收其他哨兵執行個體的IP位址的連接配接。否則哨兵不能通信和商定做什麼,故障轉移将永不會執行。

部署哨兵之前需要了解的基本事情

  1. 一個健壯的部署至少需要三個哨兵執行個體。
  2. 三個哨兵執行個體應該放置在客戶使用獨立方式确認故障的計算機或虛拟機中。例如不同的實體機或不同可用區域的虛拟機。
  3. sentinel + Redis執行個體不保證在故障期間保留确認的寫入,因為Redis使用異步複制。然而有方式部署哨兵使丢失資料限制在特定時刻,雖然有更安全的方式部署它。
  4. 你的用戶端要支援哨兵,流行的用戶端都支援哨兵,但不是全部。
  5. 沒有HA設定是安全的,如果你不經常的在開發環境測試,在生産環境他們會更好。你可能會有一個明顯的錯誤配置隻是當太晚的時候。
  6. Sentinel,Docker,或者其他形式的網絡位址交換或端口映射需要加倍小心:Docker執行端口重新映射,破壞Sentinel自動發現其他的哨兵程序和master的slave清單。稍後在這個文檔裡檢查關于Sentinel和Docker的部分,了解更多資訊。

Sentinel的配置

Redis源碼釋出包包含一個sentinel.conf的檔案,預設的配置檔案中有關于各個配置項的詳細解釋,一個典型的最小的配置檔案就像下面的配置:

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5      

上面的配置項配置了兩個名字分别為mymaster和resque的master,配置檔案隻需要配置master的資訊就好啦,不用配置slave的資訊,因為slave能夠被自動檢測到(master節點中有關于slave的消息)。

為了更清晰,我們逐行的解釋每個選項的含義:

第一行的格式如下:

sentinel monitor [master-group-name] [ip] [port] [quorum]      

master-group-name:master名稱

quorun:本文叫做票數,Sentinel需要協商同意master是否可到達的數量。

sentinel monitor mymaster 127.0.0.1 6379 2      

這一行用于告訴Redis監控一個master叫做mymaster,它的位址在127.0.0.1,端口為6379,票數是2。

這裡的票數需要解釋下:舉個栗子,redis叢集中有5個sentinel執行個體,其中master挂掉啦,如果這裡的票數是2,表示有2個sentinel認為master挂掉啦,才能被認為是正真的挂掉啦。其中sentinel叢集中各個sentinel也有互相通信,通過gossip協定。

除啦第一行其他的格式如下:

sentinel [option_name] [master_name] [option_value]      
  • down-after-milliseconds

    sentinel會向master發送心跳PING來确認master是否存活,如果master在“一定時間範圍”内不回應PONG 或者是回複了一個錯誤消息,那麼這個sentinel會主觀地認為這個master已經不可用了。而這個down-after-milliseconds就是用來指定這個“一定時間範圍”的,機關是毫秒。

  • parallel-syncs

    在發生failover主從切換時,這個選項指定了最多可以有多少個slave同時對新的master進行同步,這個數字越小,完成主從故障轉移所需的時間就越長,但是如果這個數字越大,就意味着越多的slave因為主從同步而不可用。可以通過将這個值設為1來保證每次隻有一個slave處于不能處理指令請求的狀态。

Sentinel的“仲裁會”

前面我們談到,主從故障轉移時,需要的sentinel認可的票數達到設定的值才可以。

不過,當failover主備切換真正被觸發後,failover并不會馬上進行,還需要sentinel中的大多數sentinel授權後才可以進行failover。

當sentinel認可不可用的票數達到時(ODOWN),failover被觸發。failover一旦被觸發,嘗試去進行failover的sentinel會去獲得“大多數”sentinel的授權(如果票數比大多數還要大的時候,則詢問更多的sentinel)

這個差別看起來很微妙,但是很容易了解和使用。例如,叢集中有5個sentinel,票數被設定為2,當2個sentinel認為一個master已經不可用了以後,将會觸發failover,但是,進行failover的那個sentinel必須先獲得至少3個sentinel的授權才可以實行failover。

如果票數被設定為5,要達到ODOWN狀态,必須所有5個sentinel都主觀認為master為不可用,要進行failover,那麼得獲得所有5個sentinel的授權。

配置版本号

為什麼要先獲得大多數sentinel的認可時才能真正去執行failover呢?

當一個sentinel被授權後,它将會獲得宕掉的master的一份最新配置版本号,當failover執行結束以後,這個版本号将會被用于最新的配置。因為大多數sentinel都已經知道該版本号已經被要執行failover的sentinel拿走了,是以其他的sentinel都不能再去使用這個版本号。這意味着,每次failover都會附帶有一個獨一無二的版本号。我們将會看到這樣做的重要性。

而且,sentinel叢集都遵守一個規則:如果sentinel A推薦sentinel B去執行failover,B會等待一段時間後,自行再次去對同一個master執行failover,這個等待的時間是通過

failover-timeout

配置項去配置的。從這個規則可以看出,sentinel叢集中的sentinel不會再同一時刻并發去failover同一個master,第一個進行failover的sentinel如果失敗了,另外一個将會在一定時間内進行重新進行failover,以此類推。

redis sentinel保證了活躍性:如果大多數sentinel能夠互相通信,最終将會有一個被授權去進行failover.

redis sentinel也保證了安全性:每個試圖去failover同一個master的sentinel都會得到一個獨一無二的版本号。

配置傳播

一旦一個sentinel成功地對一個master進行了failover,它将會把關于master的最新配置通過廣播形式通知其它sentinel,其它的sentinel則更新對應master的配置。

一個faiover要想被成功實行,sentinel必須能夠向選為master的slave發送

SLAVE OF NO ONE

指令,然後能夠通過

INFO

指令看到新master的配置資訊。

當将一個slave選舉為master并發送

SLAVE OF NO ONE

`後,即使其它的slave還沒針對新master重新配置自己,failover也被認為是成功了的,然後所有sentinels将會釋出新的配置資訊。

新配在叢集中互相傳播的方式,就是為什麼我們需要當一個sentinel進行failover時必須被授權一個版本号的原因。

每個sentinel使用##釋出/訂閱##的方式持續地傳播master的配置版本資訊,配置傳播的##釋出/訂閱##管道是:

__sentinel__:hello

因為每一個配置都有一個版本号,是以以版本号最大的那個為标準。

舉個栗子:假設有一個名為mymaster的位址為192.168.1.50:6379。一開始,叢集中所有的sentinel都知道這個位址,于是為mymaster的配置打上版本号1。一段時候後mymaster死了,有一個sentinel被授權用版本号2對其進行failover。如果failover成功了,假設位址改為了192.168.1.50:9000,此時配置的版本号為2,進行failover的sentinel會将新配置廣播給其他的sentinel,由于其他sentinel維護的版本号為1,發現新配置的版本号為2時,版本号變大了,說明配置更新了,于是就會采用最新的版本号為2的配置。

這意味着sentinel叢集保證了第二種活躍性:一個能夠互相通信的sentinel叢集最終會采用版本号最高且相同的配置。

SDOWN和ODOWN的更多細節

sentinel對于不可用有兩種不同的看法,一個叫主觀不可用(SDOWN),另外一個叫客觀不可用(ODOWN)。

SDOWN是sentinel自己主觀上檢測到的關于master的狀态。

ODOWN需要一定數量的sentinel達成一緻意見才能認為一個master客觀上已經宕掉,各個sentinel之間通過指令 

SENTINEL is_master_down_by_addr 

來獲得其它sentinel對master的檢測結果。

從sentinel的角度來看,如果發送了PING心跳後,在一定時間内沒有收到合法的回複,就達到了SDOWN的條件。這個時間在配置中通過 

is-master-down-after-milliseconds 

參數配置。

當sentinel發送PING後,以下回複都被認為是合法的,除此之外,其它任何回複(或者根本沒有回複)都是不合法的。

PING replied with +PONG.
PING replied with -LOADING error.
PING replied with -MASTERDOWN error.      

從SDOWN切換到ODOWN不需要任何一緻性算法,隻需要一個gossip協定:如果一個sentinel收到了足夠多的sentinel發來消息告訴它某個master已經down掉了,SDOWN狀态就會變成ODOWN狀态。如果之後master可用了,這個狀态就會相應地被清理掉。

正如之前已經解釋過了,真正進行failover需要一個授權的過程,但是所有的failover都開始于一個ODOWN狀态。

ODOWN狀态隻适用于master,對于不是master的redis節點sentinel之間不需要任何協商,slaves和sentinel不會有ODOWN狀态。

Sentinel之間和Slaves之間的自動發現機制

雖然sentinel叢集中各個sentinel都互相連接配接彼此來檢查對方的可用性以及互相發送消息。但是你不用在任何一個sentinel配置任何其它的sentinel的節點。因為sentinel利用了master的釋出/訂閱機制去自動發現其它也監控了統一master的sentinel節點。

通過向名為

__sentinel__:hello

的管道中發送消息來實作。

同樣,你也不需要在sentinel中配置某個master的所有slave的位址,sentinel會通過詢問master來得到這些slave的位址的。

每個sentinel通過向每個master和slave的釋出/訂閱頻道

__sentinel__:hello

每秒發送一次消息,來宣布它的存在。

每個sentinel也訂閱了每個master和slave的頻道

__sentinel__:hello

的内容,來發現未知的sentinel,當檢測到了新的sentinel,則将其加入到自身維護的master監控清單中。

每個sentinel發送的消息中也包含了其目前維護的最新的master配置。如果某個sentinel發現

自己的配置版本低于接收到的配置版本,則會用新的配置更新自己的master配置。

在為一個master添加一個新的sentinel前,sentinel總是檢查是否已經有sentinel與新的sentinel的程序号或者是位址是一樣的。如果是那樣,這個sentinel将會被删除,而把新的sentinel添加上去。

網絡隔離時的一緻性

redis sentinel叢集的配置的一緻性模型為最終一緻性,叢集中每個sentinel最終都會采用最高版本的配置。然而,在實際的應用環境中,有三個不同的角色會與sentinel打交道:

  • Redis執行個體.
  • Sentinel執行個體.
  • 用戶端.

為了考察整個系統的行為我們必須同時考慮到這三個角色。

下面有個簡單的例子,有三個主機,每個主機分别運作一個redis和一個sentinel:

+-------------+
             | Sentinel 1  | <--- Client A
             | Redis 1 (M) |
             +-------------+
                     |
                     |
 +-------------+     |                     +------------+
 | Sentinel 2  |-----+-- / partition / ----| Sentinel 3 | <--- Client B
 | Redis 2 (S) |                           | Redis 3 (M)|
 +-------------+                           +------------+           

在這個系統中,初始狀态下redis3是master, redis1和redis2是slave。之後redis3所在的主機網絡不可用了,sentinel1和sentinel2啟動了failover并把redis1選舉為master。

Sentinel叢集的特性保證了sentinel1和sentinel2得到了關于master的最新配置。但是sentinel3依然持着的是就的配置,因為它與外界隔離了。

當網絡恢複以後,我們知道sentinel3将會更新它的配置。但是,如果用戶端所連接配接的master被網絡隔離,會發生什麼呢?

用戶端将依然可以向redis3寫資料,但是當網絡恢複後,redis3就會變成redis的一個slave,那麼,在網絡隔離期間,用戶端向redis3寫的資料将會丢失。

也許你不會希望這個場景發生:

  • 如果你把redis當做緩存來使用,那麼你也許能容忍這部分資料的丢失。
  • 但如果你把redis當做一個存儲系統來使用,你也許就無法容忍這部分資料的丢失了。

因為redis采用的是異步複制,在這樣的場景下,沒有辦法避免資料的丢失。然而,你可以通過以下配置來配置redis3和redis1,使得資料不會丢失。

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

通過上面的配置,當一個redis是master時,如果它不能向至少一個slave寫資料(上面的min-slaves-to-write指定了slave的數量),它将會拒絕接受用戶端的寫請求。由于複制是異步的,master無法向slave寫資料意味着slave要麼斷開連接配接了,要麼不在指定時間内向master發送同步資料的請求了(上面的min-slaves-max-lag指定了這個時間)。

Sentinel狀态持久化

snetinel的狀态會被持久化地寫入sentinel的配置檔案中。每次當收到一個新的配置時,或者新建立一個配置時,配置會被持久化到硬碟中,并帶上配置的版本戳。這意味着,可以安全的停止和重新開機sentinel程序。

無failover時的配置糾正

即使目前沒有failover正在進行,sentinel依然會使用目前配置去設定監控的master。特别是:

  • 根據最新配置确認為slaves的節點卻聲稱自己是master(參考上文例子中被網絡隔離後的的redis3),這時它們會被重新配置為目前master的slave。
  • 如果slaves連接配接了一個錯誤的master,将會被改正過來,連接配接到正确的master。

Slave選舉與優先級

當一個sentinel準備好了要進行failover,并且收到了其他sentinel的授權,那麼就需要選舉出一個合适的slave來做為新的master。

slave的選舉主要會評估slave的以下幾個方面:

  • 與master斷開連接配接的次數
  • Slave的優先級
  • 資料複制的下标(用來評估slave目前擁有多少master的資料)
  • 程序ID

如果一個slave與master失去聯系超過10次,并且每次都超過了配置的最大失聯時間(

down-after-milliseconds option

),并且,如果sentinel在進行failover時發現slave失聯,那麼這個slave就會被sentinel認為不适合用來做新master的。

更嚴格的定義是,如果一個slave持續斷開連接配接的時間超過

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state      

就會被認為失去選舉資格。符合上述條件的slave才會被列入master候選人清單,并根據以下順序來進行排序:

  1. sentinel首先會根據slaves的優先級來進行排序,優先級越小排名越靠前(?)。
  2. 如果優先級相同,則檢視複制的下标,哪個從master接收的複制資料多,哪個就靠前。
  3. 如果優先級和下标都相同,就選擇程序ID較小的那個。

一個redis無論是master還是slave,都必須在配置中指定一個slave優先級。要注意到master也是有可能通過failover變成slave的。

如果一個redis的slave優先級配置為0,那麼它将永遠不會被選為master。但是它依然會從master哪裡複制資料。

Sentinel和Redis身份驗證

當一個master配置為需要密碼才能連接配接時,用戶端和slave在連接配接時都需要提供密碼。

master通過

requirepass

設定自身的密碼,不提供密碼無法連接配接到這個master。

slave通過

masterauth

來設定通路master時的密碼。

但是當使用了sentinel時,由于一個master可能會變成一個slave,一個slave也可能會變成master,是以需要同時設定上述兩個配置項。

Sentinel API

Sentinel預設運作在26379端口上,sentinel支援redis協定,是以可以使用redis-cli用戶端或者其他可用的用戶端來與sentinel通信。

有兩種方式能夠與sentinel通信:

  • 一種是直接使用用戶端向它發消息
  • 另外一種是使用釋出/訂閱模式來訂閱sentinel事件,比如說failover,或者某個redis執行個體運作出錯,等等。

Sentinel指令

sentinel支援的合法指令如下:

  • PING

     sentinel回複

    PONG

    .
  • SENTINEL masters

     顯示被監控的所有master以及它們的狀态.
  • SENTINEL master <master name>

     顯示指定master的資訊和狀态;
  • SENTINEL slaves <master name>

     顯示指定master的所有slave以及它們的狀态;
  • SENTINEL get-master-addr-by-name <master name>

     傳回指定master的ip和端口,如果正在進行failover或者failover已經完成,将會顯示被提升為master的slave的ip和端口。
  • SENTINEL reset <pattern>

     重置名字比對該正規表達式的所有的master的狀态資訊,清楚其之前的狀态資訊,以及slaves資訊。
  • SENTINEL failover <master name>

     強制sentinel執行failover,并且不需要得到其他sentinel的同意。但是failover後會将最新

動态修改Sentinel配置

從redis2.8.4開始,sentinel提供了一組API用來添加,删除,修改master的配置。

需要注意的是,如果你通過API修改了一個sentinel的配置,sentinel不會把修改的配置告訴其他sentinel。你需要自己手動地對多個sentinel發送修改配置的指令。

以下是一些修改sentinel配置的指令:

  • SENTINEL MONITOR <name> <ip> <port> <quorum> 

    這個指令告訴sentinel去監聽一個新的master
  • SENTINEL REMOVE <name>

     指令sentinel放棄對某個master的監聽
  • SENTINEL SET <name> <option> <value>

     這個指令很像Redis的

    CONFIG SET

    指令,用來改變指定master的配置。支援多個<option><value>。例如以下執行個體:
  • SENTINEL SET objects-cache-master down-after-milliseconds 1000

隻要是配置檔案中存在的配置項,都可以用

SENTINEL SET

指令來設定。這個還可以用來設定master的屬性,比如說quorum(票數),而不需要先删除master,再重新添加master。例如:

SENTINEL SET objects-cache-master quorum 5      

增加或删除Sentinel

由于有sentinel自動發現機制,是以添加一個sentinel到你的叢集中非常容易,你所需要做的隻是監控到某個Master上,然後新添加的sentinel就能獲得其他sentinel的資訊以及masterd所有的slave。

如果你需要添加多個sentinel,建議你一個接着一個添加,這樣可以預防網絡隔離帶來的問題。你可以每個30秒添加一個sentinel。最後你可以用

SENTINEL MASTER mastername

來檢查一下是否所有的sentinel都已經監控到了master。

删除一個sentinel顯得有點複雜:因為sentinel永遠不會删除一個已經存在過的sentinel,即使它已經與組織失去聯系很久了。要想删除一個sentinel,應該遵循如下步驟:

  1. 停止所要删除的sentinel
  2. 發送一個

    SENTINEL RESET * 

    指令給所有其它的sentinel執行個體,如果你想要重置指定master上面的sentinel,隻需要把*号改為特定的名字,注意,需要一個接一個發,每次發送的間隔不低于30秒。
  3. 檢查一下所有的sentinels是否都有一緻的目前sentinel數。使用

    SENTINEL MASTER mastername

     來查詢。

删除舊master或者不可達slave

sentinel永遠會記錄好一個Master的slaves,即使slave已經與組織失聯好久了。這是很有用的,因為sentinel叢集必須有能力把一個恢複可用的slave進行重新配置。

并且,failover後,失效的master将會被标記為新master的一個slave,這樣的話,當它變得可用時,就會從新master上複制資料。

然後,有時候你想要永久地删除掉一個slave(有可能它曾經是個master),你隻需要發送一個

SENTINEL RESET master

指令給所有的sentinels,它們将會更新清單裡能夠正确地複制master資料的slave。

釋出/訂閱

用戶端可以向一個sentinel發送訂閱某個頻道的事件的指令,當有特定的事件發生時,sentinel會通知所有訂閱的用戶端。需要注意的是用戶端隻能訂閱,不能釋出。

訂閱頻道的名字與事件的名字一緻。例如,頻道名為sdown 将會釋出所有與SDOWN相關的消息給訂閱者。

如果想要訂閱所有消息,隻需簡單地使用

PSUBSCRIBE *

以下是所有你可以收到的消息的消息格式,如果你訂閱了所有消息的話。第一個單詞是頻道的名字,其它是資料的格式。

注意:以下的instance details的格式是:

<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>

如果這個redis執行個體是一個master,那麼@之後的消息就不會顯示。

+reset-master <instance details> -- 當master被重置時.
    +slave <instance details> -- 當檢測到一個slave并添加進slave清單時.
    +failover-state-reconf-slaves <instance details> -- Failover狀态變為reconf-slaves狀态時
    +failover-detected <instance details> -- 當failover發生時
    +slave-reconf-sent <instance details> -- sentinel發送SLAVEOF指令把它重新配置時
    +slave-reconf-inprog <instance details> -- slave被重新配置為另外一個master的slave,但資料複制還未發生時。
    +slave-reconf-done <instance details> -- slave被重新配置為另外一個master的slave并且資料複制已經與master同步時。
    -dup-sentinel <instance details> -- 删除指定master上的備援sentinel時 (當一個sentinel重新啟動時,可能會發生這個事件).
    +sentinel <instance details> -- 當master增加了一個sentinel時。
    +sdown <instance details> -- 進入SDOWN狀态時;
    -sdown <instance details> -- 離開SDOWN狀态時。
    +odown <instance details> -- 進入ODOWN狀态時。
    -odown <instance details> -- 離開ODOWN狀态時。
    +new-epoch <instance details> -- 目前配置版本被更新時。
    +try-failover <instance details> -- 達到failover條件,正等待其他sentinel的選舉。
    +elected-leader <instance details> -- 被選舉為去執行failover的時候。
    +failover-state-select-slave <instance details> -- 開始要選擇一個slave當選新master時。
    no-good-slave <instance details> -- 沒有合适的slave來擔當新master
    selected-slave <instance details> -- 找到了一個适合的slave來擔當新master
    failover-state-send-slaveof-noone <instance details> -- 當把選擇為新master的slave的身份進行切換的時候。
    failover-end-for-timeout <instance details> -- failover由于逾時而失敗時。
    failover-end <instance details> -- failover成功完成時。
    switch-master <master name> <oldip> <oldport> <newip> <newport> -- 當master的位址發生變化時。通常這是用戶端最感興趣的消息了。
    +tilt -- 進入Tilt模式。
    -tilt -- 退出Tilt模式。      

TILT 模式

redis sentinel非常依賴系統時間,例如它會使用系統時間來判斷一個PING回複用了多久的時間。

然而,假如系統時間被修改了,或者是系統十分繁忙,或者是程序堵塞了,sentinel可能會出現運作不正常的情況。

當系統的穩定性下降時,TILT模式是sentinel可以進入的一種的保護模式。當進入TILT模式時,sentinel會繼續監控工作,但是它不會有任何其他動作,它也不會去回應

is-master-down-by-addr

這樣的指令了,因為它在TILT模式下,檢測失效節點的能力已經變得讓人不可信任了。

如果系統恢複正常,持續30秒鐘,sentinel就會退出TITL模式。

BUSY狀态

注意:該功能還未實作。

當一個腳本的運作時間超過配置的運作時間時,sentinel會傳回一個-BUSY 錯誤信号。如果這件事發生在觸發一個failover之前,sentinel将會發送一個SCRIPT KILL指令,如果script是隻讀的話,就能成功執行。

Sentinel部署示例

既然你知道了sentinel的基本資訊,你可以很想知道應該将Sentinel放置在哪裡,需要多少Sentinel程序等等。這個章節展示了幾個部署示例。

我們為了圖像化展示配置示例使用字元藝術,這是不同符号的意思:

+--------------------+
| This is a computer |
| or VM that fails   |
| independently. We  |
| call it a "box"    |
+--------------------+      

我們寫在盒子裡表示他們正在運作什麼:

+-------------------+
| Redis master M1   |
| Redis Sentinel S1 |
+-------------------+      

不同的盒子之間通過線條連接配接,表示他們可以互相通信:

+-------------+               +-------------+
| Sentinel S1 |---------------| Sentinel S2 |
+-------------+               +-------------+      

使用斜杠展示網絡斷開:

+-------------+                +-------------+
| Sentinel S1 |------ // ------| Sentinel S2 |
+-------------+                +-------------+      

還要注意:

  • Master 被叫做 M1,M2,M3 ... Mn。
  • Slave 被叫做 R1,R2,R3 ... Rn(replica的首字母)
  • Sentinels 被叫做 S1,S2,S3 ... Sn
  • Clients 被叫做 C1,C2,C3 ... Cn
  • 當一個執行個體因為Sentinel的行為改變了角色,我們把它放在方括号裡,是以[M1]表示因為Sentinel的介入,M1現在是一個master。

注意永遠不會顯示的設定隻是使用了兩個哨兵,因為為了啟動故障轉移,Sentinel總是需要和其他大多數的Sentinel通信。

執行個體1,隻有兩個Sentinel,不要這樣做

+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

Configuration: quorum = 1      
  • 在這個設定中,如果master M1故障,R1将被晉升因為兩個Sentinel可以達成協定并且還可以授權一個故障轉移因為多數就是兩個。是以他表面上看起來是可以工作的,然而檢查下一個點了解為什麼這個設定是不行的。
  • 如果運作M1的盒子停止工作了,S1也停止工作。運作在其他盒子上的S2将不能授權故障轉移,是以系統将變成不可用。

注意為了排列不同的故障轉移需要少數服從多數,并且稍後向所有的Sentinel傳播最新的配置。還要注意上面配置的故障轉移的能力,沒有任何協定,非常危險:

+----+           +------+
| M1 |----//-----| [M1] |
| S1 |           | S2   |
+----+           +------+      

在上面的配置中我們使用完美的對稱方式建立了兩個master(假定S2可以在未授權的情況下進行故障轉移)。用戶端可能會不确定往哪邊寫,并且沒有途徑知道什麼時候分區配置是正确的,為了預防一個永久的斷裂狀态。

所有請永遠部署至少三個Sentinel在三個不同的盒子裡。

例2:使用三個盒子的基本設定

這是個非常簡單的設定,它有簡單調整安全的優勢。它基于三個盒子,每個盒子同時運作一個Redis執行個體和一個Sentinel執行個體。

+----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

Configuration: quorum = 2      

如果M1故障,S2和S3将會商定故障并授權故障轉移,使用戶端可以繼續。

在每個Sentinel設定裡,Redis是異步主從複制,總會有丢失資料的風險,因為有可能當它成為master的時候,一個确認的寫入操作還沒有同步到slave。然後在上面的設定中有一個更高的風險由于用戶端分區一直是老的master,就像下面的圖像所示: 

+----+
         | M1 |
         | S1 | [- C1 (writes will be lost)
         +----+
            |
            /
            /
+------+    |    +----+
| [M2] |----+----| R3 |
| S2   |         | S3 |
+------+         +----+      

在這個案例中網絡分區隔離老的master M1,是以slave R2晉升為master。然而用戶端,比如C1,還在原來的老的master的分區,可能繼續往老master寫資料。這個資料将會永久丢失,因為分區恢複時,master将會重新配置為新master的slave,丢棄它的資料集。

這個問題可以使用下面的Redis主從複制特性減輕,它可在master檢查到它不再能傳輸它的寫入操作到指定數量的slave的時候停止接收寫入操作。

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

使用上面的配置(請檢視自帶的redis.conf示例了解更多資訊)一個Redis執行個體,當作為一個master,如果它不能寫入至少1個slave将停止接收寫入操作。(N個Slave以上不通就停止接收)

由于主從複制是異步的不能真實的寫入,意味着slave斷開連接配接,或者不再向我們發送異步确認的指定的max-lag秒數。(判定連接配接不通的逾時時間)

在上面的示例中使用這個配置,老master M1将會在10秒鐘後變為不可用。當分區恢複時,Sentinel配置将指向新的一個,用戶端C1将能夠擷取到有效的配置并且将使用新master繼續工作。

然而天下沒有免費的午餐,這種改進,如果兩個slave挂掉,master将會停止接收寫入操作。這是個權衡。

例三:Sentinel在用戶端盒子裡

有時我們隻有兩個Redis盒子可用,一個master和一個slave。在例二中的配置在那樣的情況下是不可行的,所謂我們可以借助下面的,Sentinel放置在用戶端: 

+----+         +----+
            | M1 |----+----| R1 |
            | S1 |    |    | S2 |
            +----+    |    +----+
                      |
         +------------+------------+
         |            |            |
         |            |            |
      +----+        +----+      +----+
      | C1 |        | C2 |      | C3 |
      | S1 |        | S2 |      | S3 |
      +----+        +----+      +----+

      Configuration: quorum = 2      

在這個設定裡,Sentinel的視角和用戶端的視角相同:如果大多數的用戶端認為master是可以到達的,它就是好的。C1,C2,C3是一般的用戶端,這不意味着C1識别單獨的用戶端連接配接到Redis。它更像一些如應用服務,Rails應用之類的。

如果運作M1和S1的盒子故障,故障轉移将會發生,然而很容看到不同的網絡分區将導緻不同的行為。例如如果用戶端和Redis服務之間的斷開連接配接,Sentinel将不能設定,因為master和slave将都不可用。

注意如果使用M1擷取分區,我們有一個和例二中描述的相似的問題,不同的是這裡我們沒有辦法打破對稱,由于隻有一個slave和master,是以當它的master斷開連接配接時master不能停止接收查詢,否則在slave故障期間master将永不可用。

是以這是個有效的設定但是在例二中的設定有像更容易管理HA系統的優點, 并有能力限制master接收寫入的時間。

例4:少于3個用戶端的Sentinel用戶端

在例3中如果用戶端少于3個就不能使用。在這個案例中我們使用一個混合的設定:

+----+         +----+
            | M1 |----+----| R1 |
            | S1 |    |    | S2 |
            +----+    |    +----+
                      |
               +------+-----+
               |            |  
               |            |
            +----+        +----+
            | C1 |        | C2 |
            | S3 |        | S4 |
            +----+        +----+

      Configuration: quorum = 3      

這裡和例3非常類似,但是這裡我們在4個盒子裡運作四個哨兵。如果M1故障其他的三個哨兵可以執行故障轉移。

本文參考文檔:

https://redis.io/topics/sentinel

http://redis.majunwei.com/topics/sentinel.html

https://segmentfault.com/a/1190000002680804

https://segmentfault.com/a/1190000002685515

總結

接下來是大家最喜歡的總結内容啦,内容有二,如下:

1、希望能關注我其他的文章。

2、部落格裡面有沒有很清楚的說明白,或者你有更好的方式,那麼歡迎加入左上方的2個交流群,我們一起學習探讨。

繼續閱讀