序言
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地址的串連。否則哨兵不能通訊和商定做什麼,容錯移轉將永不會執行。 部署哨兵之前需要瞭解的基本事情
一個健壯的部署至少需要三個哨兵執行個體。
三個哨兵執行個體應該放置在客戶使用獨立方式確認故障的電腦或虛擬機器中。例如不同的物理機或不同可用性區域域的虛擬機器。
sentinel + Redis執行個體不保證在故障期間保留確認的寫入,因為Redis使用非同步複製。然而有方式部署哨兵使遺失資料限制在特定時刻,雖然有更安全的方式部署它。
你的用戶端要支援哨兵,流行的用戶端都支援哨兵,但不是全部。
沒有HA設定是安全的,如果你不經常的在開發環境測試,在生產環境他們會更好。你可能會有一個明顯的錯誤配置只是當太晚的時候。
Sentinel,Docker,或者其他形式的網路地址交換或連接埠映射需要加倍小心:Docker執行連接埠重新對應,破壞Sentinel自動探索其他的哨兵進程和master的slave列表。稍後在這個文檔裡檢查關於Sentinel和Docker的部分,瞭解更多資訊。 Sentinel的配置
Redis源碼發布包包含一個sentinel.conf的檔案,預設的設定檔中有關於各個配置項的詳細解釋,一個典型的最小的設定檔就像下面的配置:
sentinel monitor mymaster 127.0.0.1 6379 2sentinel down-after-milliseconds mymaster 60000sentinel failover-timeout mymaster 180000sentinel parallel-syncs mymaster 1sentinel monitor resque 192.168.1.3 6380 4sentinel down-after-milliseconds resque 10000sentinel failover-timeout resque 180000sentinel 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 1min-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候選人列表,並根據以下順序來進行排序:
sentinel首先會根據slaves的優先順序來進行排序,優先順序越小排名越靠前(。)。
如果優先順序相同,則查看複製的下標,哪個從master接收的複製資料多,哪個就靠前。
如果優先順序和下標都相同,就選擇進程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> 重設名字匹配該Regex的所有的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,應該遵循如下步驟:
停止所要刪除的sentinel
發送一個SENTINEL RESET * 命令給所有其它的sentinel執行個體,如果你想要重設指定master上面的sentinel,只需要把*號改為特定的名字,注意,需要一個接一個發,每次發送的間隔不低於30秒。
檢查一下所有的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>