天天看點

redis的sentinel

概述

Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案,當用Redis做Master-slave的高可用方案時,假如master當機了,Redis本身(包括它的很多用戶端)都沒有實作自動進行主備切換,而Redis-sentinel本身也是一個獨立運作的程序,它能監控多個master-slave叢集,發現master當機後能進行自懂切換。

它的主要功能有以下幾點

  • 不時地監控redis是否按照預期良好地運作;
  • 如果發現某個redis節點運作出現狀況,能夠通知另外一個程序(例如它的用戶端);
  • 能夠進行自動切換。當一個master節點不可用時,能夠選舉出master的多個slave(如果有超過一個slave的話)中的一個來作為新的master,其它的slave節點會将它所追随的master的位址改為被提升為master的slave的新位址。<br/>

Sentinel支援叢集

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

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

Sentinel版本

Sentinel目前最新的穩定版本稱為Sentinel 2(與之前的Sentinel 1區分開來)。随着redis2.8的安裝包一起發行。安裝完Redis2.8後,可以在redis2.8/src/裡面找到Redis-sentinel的啟動程式。

強烈建議:

如果你使用的是redis2.6(sentinel版本為sentinel 1),你最好應該使用redis2.8版本的sentinel 2,因為sentinel 1有很多的Bug,已經被官方棄用,是以強烈建議使用redis2.8以及sentinel 2。

運作Sentinel

運作sentinel有兩種方式:

  • 第一種
    redis-sentinel /path/to/sentinel.conf
               
  • 第二種

    以上兩種方式,都必須指定一個sentinel的配置檔案sentinel.conf,如果不指定,将無法啟動sentinel。sentinel預設監聽26379端口,是以運作前必須确定該端口沒有被别的程序占用。

Sentinel的配置

Redis源碼包中包含了一個sentinel.conf檔案作為sentinel的配置檔案,配置檔案自帶了關于各個配置項的解釋。典型的配置項如下所示:

sentinel monitor mymaster   
sentinel down-after-milliseconds mymaster 
sentinel failover-timeout mymaster 
sentinel parallel-syncs mymaster 

sentinel monitor resque   
sentinel down-after-milliseconds resque 
sentinel failover-timeout resque 
sentinel parallel-syncs resque 
           

上面的配置項配置了兩個名字分别為mymaster和resque的master,配置檔案隻需要配置master的資訊就好啦,不用配置slave的資訊,因為slave能夠被自動檢測到(master節點會有關于slave的消息)。需要注意的是,配置檔案在sentinel運作期間是會被動态修改的,例如當發生主備切換時候,配置檔案中的master會被修改為另外一個slave。這樣,之後sentinel如果重新開機時,就可以根據這個配置來恢複其之前所監控的redis叢集的狀态。

接下來我們将一行一行地解釋上面的配置項:

sentinel monitor mymaster   
           

這一行代表sentinel監控的master的名字叫做mymaster,位址為127.0.0.1:6379,行尾最後的一個2代表什麼意思呢?我們知道,網絡是不可靠的,有時候一個sentinel會因為網絡堵塞而誤以為一個master redis已經死掉了,當sentinel叢集式,解決這個問題的方法就變得很簡單,隻需要多個sentinel互相溝通來确認某個master是否真的死了,這個2代表,當叢集中有2個sentinel認為master死了時,才能真正認為該master已經不可用了。(sentinel叢集中各個sentinel也有互相通信,通過gossip協定)。

除了第一行配置,我們發現剩下的配置都有一個統一的格式:

接下來我們根據上面格式中的option_name一個一個來解釋這些配置項:

  • down-after-milliseconds

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

不過需要注意的是,這個時候sentinel并不會馬上進行failover主備切換,這個sentinel還需要參考sentinel叢集中其他sentinel的意見,如果超過某個數量的sentinel也主觀地認為該master死了,那麼這個master就會被客觀地(注意哦,這次不是主觀,是客觀,與剛才的subjectively down相對,這次是objectively down,簡稱為ODOWN)認為已經死了。需要一起做出決定的sentinel數量在上一條配置中進行配置。
  • parallel-syncs

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

其他配置項在sentinel.conf中都有很詳細的解釋。

所有的配置都可以在運作時用指令

SENTINEL SET command

動态修改。

Sentinel的“仲裁會”

前面我們談到,當一個master被sentinel叢集監控時,需要為它指定一個參數,這個參數指定了當需要判決master為不可用,并且進行failover時,所需要的sentinel數量,本文中我們暫時稱這個參數為票數

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

當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   | <--- Client A
             | Redis  (M) |
             +-------------+
                     |
                     |
 +-------------+     |                     +------------+
 | Sentinel   |-----+-- / partition / ----| Sentinel  | <--- Client B
 | Redis  (S) |                           | Redis  (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 
min-slaves-max-lag 
           

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

Sentinel狀态持久化

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

繼續閱讀