Redis:哨兵(sentinel)
redis:持久化
redis:主從復制
redis:哨兵
redis:集群
1 概述
1.1 作用
從宏觀角度回顧一下Redis實現高可用相關的技術。它們包括:持久化、復制、哨兵和集群,其主要作用和解決的問題是:
- 持久化:持久化是最簡單的高可用方法(有時甚至不被歸為高可用的手段),主要作用是數據備份,即將數據存儲在硬盤,保證數據不會因進程退出而丟失。
- 復制:復制是高可用Redis的基礎,哨兵和集群都是在復制基礎上實現高可用的。復制主要實現了數據的多機備份,以及對于讀操作的負載均衡和簡單的故障恢復。缺陷:故障恢復無法自動化;寫操作無法負載均衡;存儲能力受到單機的限制。
- 哨兵:在復制的基礎上,哨兵實現了自動化的故障恢復。缺陷:寫操作無法負載均衡;存儲能力受到單機的限制。
- 集群:通過集群,Redis解決了寫操作無法負載均衡,以及存儲能力受到單機限制的問題,實現了較為完善的高可用方案。
哨兵功能:哨兵的核心功能是主節點的自動故障轉移。下面是Redis官方文檔對于哨兵功能的描述:
- 監控(Monitoring):哨兵會不斷地檢查主節點和從節點是否運作正常。
- 自動故障轉移(Automatic failover):當主節點不能正常工作時,哨兵會開始自動故障轉移操作,它會將失效主節點的其中一個從節點升級為新的主節點,并讓其他從節點改為復制新的主節點。
- 配置提供者(Configuration provider):客戶端在初始化時,通過連接哨兵來獲得當前Redis服務的主節點地址。
- 通知(Notification):哨兵可以將故障轉移的結果發送給客戶端。
其中,監控和自動故障轉移功能,使得哨兵可以及時發現主節點故障并完成轉移;而配置提供者和通知功能,則需要在與客戶端的交互中才能體現。
1.2 架構
它由兩部分組成,哨兵節點和數據節點:
- 哨兵節點:哨兵系統由一個或多個哨兵節點組成,哨兵節點是特殊的redis節點,不存儲數據。
- 數據節點:主節點和從節點都是數據節點。
2 部署
2.1 部署主從節點
在上一篇文章中已經寫過部署主從節點,使用主從復制。
redis主從節點部署
2.2 部署sentinel節點
哨兵節點本質上是特殊的Redis節點。
3個哨兵節點的配置幾乎是完全一樣的,主要區別在于端口號的不同(26379/26380/26381),下面以26379節點為例介紹節點的配置和啟動方式;
#sentinel-26379.conf port 26379 daemonize yes logfile "26379.log" sentinel monitor mymaster 192.168.92.128 6379 2 sentinel auth-pass mymaster 密碼其中,sentinel monitor mymaster 192.168.92.128 6379 2 配置的含義是:該哨兵節點監控192.168.92.128:6379這個主節點,該主節點的名稱是mymaster(自己配置的),最后的2的含義與主節點的故障判定有關:至少需要2個哨兵節點同意,才能判定主節點故障并進行故障轉移。
sentinel auth-pass mymaster 密碼 配置的含義是:如果主節點有密碼需要在此配置密碼。
哨兵節點的啟動有兩種方式,二者作用是完全相同的:
redis-sentinel sentinel-26379.conf redis-server sentinel-26379.conf --sentinel按照上述方式配置和啟動之后,整個哨兵系統就啟動完畢了。可以通過redis-cli連接哨兵節點進行驗證,如下圖所示:可以看出26379哨兵節點已經在監控mymaster主節點,并發現了其2個從節點和另外2個哨兵節點
此時在看sentinel的配置文件,以端口為26379的配置文件為例:
其中,dir只是顯式聲明了數據和日志所在的目錄(在哨兵語境下只有日志);
known-slave和known-sentinel顯示哨兵已經發現了從節點和其他哨兵;
帶有epoch的參數與配置紀元有關(配置紀元是一個從0開始的計數器,每進行一次領導者哨兵選舉,都會+1;領導者哨兵選舉是故障轉移階段的一個操作,在后文會介紹)。
3 sentinel初始化
啟動一個sentinel 使用命令:
redis-sentinel sentinel-26379.conf redis-server sentinel-26379.conf --sentinel //這兩個命令的效果完成相同當一個Sentinel啟動時,他需要執行以下步驟:
3.1 初始化服務器
首先,因為Sentinel 本質上只是一個運行在特殊模式下的Redis服務器,所以啟動Sentinel的第一步,就是初始化一個普通的Redis服務器。
不過,因為Sentinel執行的工作和普通Redis服務器執行的工作不同,所以Sentinel的初始化過程和普通Redis服務器的初始化過程并不完全相同。
例如,普通服務器在初始化時會通過載人RDB文件或者AOF文件來還原數據庫狀態,但是因為Sentinel并不使用數據庫,所以初始化Sentinel時就不會載人RDB文件或者AOF文件。
表16-1展示了Redis服務器在Sentinel模式下運行時,服務器各個主要功能的使用情況。
3.2 使用sentinel專用代碼
啟動Sentinel的第二個步驟就是將一部分普通的Redis服務器使用的代碼替換成Sentinel專用代碼。
例如:普通Redis服務器使用redis.c/redisCommandTable作為服務器的命令表:
Sentinel則使用sentinel.c/sentinelcmds作為服務器的命令表,并且其中的INFO命令會使用Sentinel模式下的專用實現sentinel.c/sentinelInfoCommand函數而不是普通redis服務器使用的實現redis.c/infoCommand函數:
這也說明了為什么在Sentinel模式下,Redis服務器不能執行set,eval等命令,因為命令表中沒有載入這些命令。
3.3 初始化Sentinel狀態
接下來,服務器會初始化sentinel.c/sentinelState結構(叫做,Sentinel狀態),這個結構保存了服務器所有和Sentinel功能有關的狀態。
3.4 初始化Sentinel狀態的master屬性
初始化Sentinel狀態的masters屬性。Sentinel狀態中的masters字典記錄了所有被Sentinel監視的主服務器的相關信息,其中:
字典的鍵是被監視主服務器的名字。
而字典的值則是被監視主服務器對應的sentinel. c/ sentinelRedisInstance結構。
每個sentinelRedisInstance結構( 后面簡稱“實例結構”)代表一個被Sentinel監視
的Redis服務器實例( instance),這個實例可以是主服務器、從服務器,或者另外一個Sentinel。
3.5 創建連向主服務器的網絡連接
初始化Sentinel的最后一步是創建連向被監視服務器的網絡連接,Sentinel將成為主服務器的客戶端,他可以向主服務器發送命令,并從命令恢復中獲取相關信息。
創建兩個連向主服務器的異步網絡連接:
為什么有兩個連接?
在Redis目前的發布與訂閱功能中,被發送的信息都不會保存在Redis服務器里面,如果在信息發送時,想要接收信息的客戶端不在線或者斷線,那么這個客戶端就會丟失這條信息。因此,為了不丟失_ sentine1__ :he1lo 頻道的任何信息,Sentinel 必須專門用一個訂閱連接來接收該頻道的信息。
另一方面,除了訂閱頻道之外,Sentinel 還必須向主服務器發送命令,以此來與主服務器進行通信,所以Sentinel還必須向主服務器創建命令連接。
因為Sentinel需要與多個實例創建多個網絡連接,所以Sentinel 使用的是異步連接。
4 基本原理
4.1 發送info信息
4.1.1 向主服務器發送
Sentinel默認會以每10秒一次的頻率,通過命令連接向被監視的主服務器發送INFO命令,并通過分析INFO命令的回復來獲取主服務器的當前信息。
通過分析主服務器返回的INFO命令回復,Sentinel可以獲取兩方面的信息:
4.1.2 向從服務發送
當Sentinel發現主服務器有新的從服務器出現時,Sentinel會為這個新的從服務器創建相應的實例結構(實例結構保存在主服務器的實例結構中),Sentinel還會創建連接到從服務器的命令連接和訂閱連接。
創建命令連接之后,Sentinel會和對主服務器一樣,在默認的情況下,會以每10秒一次的頻率發送INFO命令,獲取類似以下信息:
獲取到信息,Sentinel會對保存的從服務器實例結構更新。
4.2 發送publish信息
在默認情況下,Sentinel會以每兩秒一次的頻率,通過命令連接向所有被監視的主服務器和從服務器發送以下格式的命令:
這條命令向服務器的_sentinel_:hello 頻道發送了一條信息。信息中包括sentinel和主服務器的相關信息。
4.3 接收頻道信息
Sentinel對_sentinel_:hello頻道的訂閱會一直持續到Sentinel與服務器的連接斷開為止。
對于每一個Sentinel連接的服務器,Sentinel既可以通過命令連接向服務器的_sentinel_:hello頻道發送信息,又可以通過訂閱連接從服務器的_sentinel_:hello頻道接收信息。
對于監視同一個服務器的多個Sentinel來說,一個Sentinel發送的信息會被其他Sentinel(包括自己)接收到,這些信息可以用于更新其他Sentinel對發送信息Sentinel的認知,也會更新其他Sentinel對被監視器服務器的認知。
Sentinel接收到自己的信息會不做處理,如果是其他Sentinel信息會對監視服務器的實例進行更新保存其他Sentinel的信息。
當Sentinel通過頻道信息發現一個新的Sentinel時,不僅會為新的Sentinel在監視服務器的實例結構的sentinels字典中創建相應的實例結構,還會創建一個連向新Sentinel的命令連接,而新的Sentinel也會同樣創建連向這個Sentinel的命令連接,最終監視同一主服務器的多個Sentinel將形成相互連接的網絡:
Sentinel之間不會創建訂閱連接
Sentinel在連接主服務器或者從服務器時,會同時創建命令連接和訂閱連接,但是在連接其他Sentinel時,卻只會創建命令連接,而不創建訂閱連接。這是因為Sentinel需要通過接收主服務器或者從服務器發來的頻道信息來發現未知的新Sentinel,所以才需要建立訂閱連接,而相互已知的Sentinel只要使用命令連接來進行通信就足夠了。
4.4 檢測服務器狀態
4.4.1 檢測主觀下線狀態
在默認情況下,Sentinel會以每秒一次的頻率向所有與它創建了命令連接的實例(包括主服務器,從服務器,其他Sentinel在內)發送PING命令,并通過實例返回的PING命令回復來判斷實例是否在線。
實例對PING命令的回復可以分為以下兩種情況:
Sentinel配置文件中的down-after-milliseconds選項指定了Sentinel判斷實例進入主觀下線所需的時間長度:如果一個實例在down-after-millsecods毫秒內,連續向Sentinel返回無效回復,那么Sentinel會修改這個實例所對應的實例結構,在結構的flags屬性中打開SRI_S_DOWN標識,以此來表示這個實例已經進入主觀下線狀態。
這個選項不僅作為監視的主服務器的判斷主觀下線狀態,也是主服務器的從服務器,以及其他的Sentinel判斷下線的狀態。
4.4.2 檢查客觀下線狀態
需要特別注意的是,客觀下線是主節點才有的概念;如果從節點和哨兵節點發生故障,被哨兵主觀下線后,不會再有后續的客觀下線和故障轉移操作。
當Sentinel 將一個主服務器判斷為主觀下線之后,為了確認這個主服務器是否真的下線了,它會向同樣監視這一主服務器的其他Sentinel進行詢問,看它們是否也認為主服務器已經進入了下線狀態(可以是主觀下線或者客觀下線)。當Sentinel從其他Sentinel那里接收到足夠數量(這個數量是在Sentinel中配置的)的已下線判斷之后,Sentinel 就會將從服務器判定為客觀下線,并對主服務器執行故障轉移操作。
4.5 選舉領頭Sentinel
當一個主服務器被判斷為客觀下線時,監視這個下線主服務器的各個Sentinel會進行協商,選舉出一個領頭的Sentinel,并由領頭Sentinel對下線主服務器執行故障轉移操作。
在一次配置紀元中,發現主服務器客觀下線的Sentinel會要求其他監視主服務器的Sentinel選舉自己為局部領頭Sentinel(向其他Sentinel發送信息,最先接收到哪個Sentinel的信息就選舉哪個Sentinel為局部領頭Sentinel),如果超過所有Sentinel數量的一半,那么就選舉成功,配置紀元+1進行領頭Sentinel故障轉移,如果都沒有超過一半,那么配置紀元+1,再次進行選擇。
4.6 故障轉移
在選舉產生出領頭Sentinel之后,領頭Sentinel將對已下線的主服務器執行故障轉移操
作,該操作包含以下三個步驟:
(1) 在已下線主服務器屬下的所有從服務器里面,挑選出一個從服務器,并將其轉換為
主服務器。
故障轉移操作第一步 要做的就是在已下線主服務器屬下的所有從服務器中,挑選出一個狀態良好、數據完整的從服務器,然后向這個從服務器發送SLAVEOF no one命令,將這個從服務器轉換為主服務器。
補充:redis設計與實現
(2)讓已下線主服務器屬下的所有從服務器改為復制新的主服務器。
當新的主服務器出現之后,領頭Sentinel下一步要做的就是,讓已下線主服務器屬下的所有從服務器去復制新的主服務器,這一動作可以通過向從服務器發送SLAVEOF命令來實現。
(3)將已下線主服務器設置為新的主服務器的從服務器,當這個舊的主服務器重新上線
時,它就會成為新的主服務器的從服務器。
故障轉移的最后就是,將已下線的主服務器設置為新的主服務器的從服務器。
5 相關配置和實踐建議
搬運文檔
5.1 相關配置
與哨兵相關的幾個配置。
(1) sentinel monitor {masterName} {masterIp} {masterPort} {quorum}
sentinel monitor是哨兵最核心的配置,在前文講述部署哨兵節點時已說明,其中:masterName指定了主節點名稱,masterIp和masterPort指定了主節點地址,quorum是判斷主節點客觀下線的哨兵數量閾值:當判定主節點下線的哨兵數量達到quorum時,對主節點進行客觀下線。建議取值為哨兵數量的一半加1。
(2) sentinel down-after-milliseconds {masterName} {time}
sentinel down-after-milliseconds與主觀下線的判斷有關:哨兵使用ping命令對其他節點進行心跳檢測,如果其他節點超過down-after-milliseconds配置的時間沒有回復,哨兵就會將其進行主觀下線。該配置對主節點、從節點和哨兵節點的主觀下線判定都有效。
down-after-milliseconds的默認值是30000,即30s;可以根據不同的網絡環境和應用要求來調整:值越大,對主觀下線的判定會越寬松,好處是誤判的可能性小,壞處是故障發現和故障轉移的時間變長,客戶端等待的時間也會變長。例如,如果應用對可用性要求較高,則可以將值適當調小,當故障發生時盡快完成轉移;如果網絡環境相對較差,可以適當提高該閾值,避免頻繁誤判。
(3) sentinel parallel-syncs {masterName} {number}
sentinel parallel-syncs與故障轉移之后從節點的復制有關:它規定了每次向新的主節點發起復制操作的從節點個數。例如,假設主節點切換完成之后,有3個從節點要向新的主節點發起復制;如果parallel-syncs=1,則從節點會一個一個開始復制;如果parallel-syncs=3,則3個從節點會一起開始復制。
parallel-syncs取值越大,從節點完成復制的時間越快,但是對主節點的網絡負載、硬盤負載造成的壓力也越大;應根據實際情況設置。例如,如果主節點的負載較低,而從節點對服務可用的要求較高,可以適量增加parallel-syncs取值。parallel-syncs的默認值是1。
(4) sentinel failover-timeout {masterName} {time}
sentinel failover-timeout與故障轉移超時的判斷有關,但是該參數不是用來判斷整個故障轉移階段的超時,而是其幾個子階段的超時,例如如果主節點晉升從節點時間超過timeout,或從節點向新的主節點發起復制操作的時間(不包括復制數據的時間)超過timeout,都會導致故障轉移超時失敗。
failover-timeout的默認值是180000,即180s;如果超時,則下一次該值會變為原來的2倍。
5.2 實踐建議
(1)哨兵節點的數量應不止一個,一方面增加哨兵節點的冗余,避免哨兵本身成為高可用的瓶頸;另一方面減少對下線的誤判。此外,這些不同的哨兵節點應部署在不同的物理機上。
(2)哨兵節點的數量應該是奇數,便于哨兵通過投票做出“決策”:領導者選舉的決策、客觀下線的決策等。
(3)各個哨兵節點的配置應一致,包括硬件、參數等;此外,所有節點都應該使用ntp或類似服務,保證時間準確、一致。
(4)哨兵的配置提供者和通知客戶端功能,需要客戶端的支持才能實現,如前文所說的Jedis;如果開發者使用的庫未提供相應支持,則可能需要開發者自己實現。
(5)當哨兵系統中的節點在docker(或其他可能進行端口映射的軟件)中部署時,應特別注意端口映射可能會導致哨兵系統無法正常工作,因為哨兵的工作基于與其他節點的通信,而docker的端口映射可能導致哨兵無法連接到其他節點。例如,哨兵之間互相發現,依賴于它們對外宣稱的IP和port,如果某個哨兵A部署在做了端口映射的docker中,那么其他哨兵使用A宣稱的port無法連接到A。
6 總結
在主從復制的基礎上,哨兵引入了主節點的自動故障轉移,進一步提高了Redis的高可用性;但是哨兵的缺陷同樣很明顯:哨兵無法對從節點進行自動故障轉移,在讀寫分離場景下,從節點故障會導致讀服務不可用,需要我們對從節點做額外的監控、切換操作。
此外,哨兵仍然沒有解決寫操作無法負載均衡、及存儲能力受到單機限制的問題;這些問題的解決需要使用集群。
7 參考文檔
https://www.cnblogs.com/kismetv/p/9609938.html
redis的設計與實現
總結
以上是生活随笔為你收集整理的Redis:哨兵(sentinel)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一些不错的书
- 下一篇: python 如何安装软件包故障_安装