Redis基礎篇(七)哨兵機制
上一篇文章介紹了高可靠方案:主從集群模式。通過主從庫的讀寫分離,來保證服務的可靠性。
當某個從庫出現故障時,不影響服務的使用,主庫仍然可以處理寫命令,其他從庫可以處理讀命令。但主庫發生故障,就不能處理寫命令了,從庫只能處理讀命令。這就影響服務的正常使用了,該如何解決呢?
只要找一個從庫當主庫就可以解決了。但還有三個問題需要處理:
- 主庫真的掛了嗎?
- 該選擇哪個從庫作為主庫?
- 怎麼把新主庫的相關信息通知給從庫和客戶端?
這裡就要介紹Redis的哨兵機制了。哨兵機制指在Redis主從集群模式下,實現主從庫自動切換。它有效地解決了主從集群模式下故障時的三個問題。
哨兵機制的基本流程
哨兵其實是一個運行在特殊模式下的Redis進行,主從庫實例運行的同時,它也在運行。哨兵主要負責的任務有三個:監牢、選主(選擇主庫)和通知。如下圖所示:
- 監控:哨兵進程周期性地給所有主從庫發送PING命令,檢測它們是否在線運行
- 選主(選擇主庫):選出新主庫
- 通知:讓從庫執行replicaof,與新主庫同步;通知客戶端,與新主庫連接。
通知任務相對比較簡單且容易理解,但在監控和選主這兩個任務中,哨兵需要做出兩個決策:
- 在監控任務中,哨兵需要判斷主庫是否處於下線狀態;
- 在選主任務中,哨兵要決定選擇哪個從庫實例作為主庫。
下面就介紹一下這兩個任務。
哨兵是如何判斷主庫是否下線
哨兵對主庫的下線狀態判斷有「主觀下線」和「客觀下線」兩種。
- 主線下線,哨兵發現主庫或從庫對PING命令的響應超時。
- 客觀下線,表示主庫下線是一個客觀事實。
哨兵在判斷主從庫採用不同方式:
- 哨兵PING從庫,如果超時,就直接標記「主觀下線」
- 哨兵PING主庫,超時不能直接標記「主觀下線」,因為可能由於網絡阻塞等原因導致誤判。
如何解決哨兵誤判?
通過哨兵集群,也就是由多個哨兵組成的集群來進行判斷。採用少數服從多數,超過N/2+1判斷主庫為「主觀下線」,那就判斷主庫為「客觀下線」。關於哨兵集群,下面會詳細介紹。
哨兵是如何選定新主庫的?
哨兵選定新主庫用四個字概括:「篩選+打分」。簡單來說,就是根據篩選條件選出候選從庫,然後通過打分,選出最高分的作為新主庫。下面說一下篩選條件和打分規則。
篩選條件
首先從庫必須在線運行。
其次從庫網絡狀態良好。從庫和主庫斷連超出一定的閾值就把這個從庫篩掉。這個閾值就是down-after-milliseconds
,表示主從庫斷連的最大連接超時時間。例如發生超過10次,就認定從庫的網絡狀況不好。
關於down-after-milliseconds
,值越小,哨兵就越敏感。當網絡擁塞但主庫正常,可能會發生不必要的切換。而當主庫真的故障了,就切換及時,對業務影響最小。
因此down-after-milliseconds
要設置合適的值,既減少不必要的切換,也保證能夠及時切換,降低對業務的影響。
打分規則
第一輪,優先級最高的從庫得分高。可通過slave-priority
配置項,設置從庫優先級。
第二輪,和舊主庫同步程度最接近的從庫得分高。很容易理解,越接近主庫,說明數據丟失越少。前面介紹主從複製時,已經知道主從庫是通過repl_backlog_buffer
保持同步的,所以slave_repl_offset
最接近master_repl_offset
,得分高。
第三輪,ID號小的從庫得分高。每個實例都會有一個ID。這是最後一輪,必須決出勝負,所以就選擇最小ID的作為新主庫。
到這裡,我們對哨兵機制的基本流程有了一個整體的認識,下面我們再來了解關於主從切換的兩個問題。
在做主從切換時,客戶端能否正常地進行請求操作?
如果客戶端使用了讀寫分離,那麼讀請求不受影響,而寫請求會失敗。失敗持續時間 = 哨兵切換主從的時間 + 客戶端感知到新主庫的時間。
如果不想讓業務感知到異常,那客戶端只能把寫失敗的請求先緩存起來,或者寫入消息隊列中間件。這種只適合對寫入請求返回值不敏感的業務。
應用程序不感知服務中斷,哨兵和客戶端要做什麼?
當哨兵完成主從切換後,客戶端需要及時感知到主庫發生了變更,然後把緩存的寫請求寫入到新庫中,保證後續寫請求不會再受到影響。具體做法有兩方面:
一方面是哨兵主要通知客戶端。哨兵在選主後,會把新主庫的地址寫入自己實例的pub/sub里,客戶端需要訂閱pub/sub。當切換新主庫後,客戶端就能拿到新主庫的地址,把寫請求發到這個新主庫即可。
另一方面是客戶端主動獲取最新的主從地址。如果客戶端因為某些原因錯過了哨兵的通知,或者哨兵通知後客戶端處理失敗了,就需要客戶端主動獲取。
下面再來學習哨兵集群。
哨兵集群
如果哨兵實例在運行時發生故障,主從庫還能正常切換嗎?
通常,我們在解決一個系統問題的時候,會引入一個新機制,或者設計一層新功能。這裡Redis引入哨兵集群來解決哨兵實例的高可靠性問題。
基於pub/sub機制組成哨兵集群
哨兵實例之間可以互相發現,靠的是Redis提供的pub/sub機制,也就是發佈/訂閱機制。
哨兵和主庫建立連接,就可以在主庫上發佈消息了,比如發佈自己的連接信息(IP和端口),同時它也會從主庫上訂閱消息,獲得其他哨兵的連接信息。
當多個哨兵實例都在主庫上做了發佈和訂閱操作,它們之間就能知道彼此的IP地址和端口了。
哨兵除了彼此之間建立起連接形成集群外,還需要和從庫建立連接。因為在哨兵的監控任務中,它需要對主從庫都進行監控,而且在主從庫切換完成後,還需要通知從庫,讓它們和新主庫進行同步。
那麼,哨兵是如何知道從庫的IP地址和端口的呢?
哨兵通過向主庫發送INFO命令來完成的。如下圖所示:
通過pub/sub機制,哨兵之間可以組成集群;通過INFO命令,哨兵和從庫建立連接,並進行監控。
但是哨兵不能只和主、從庫連接。因為,主從庫切換後,客戶端也需要知道新主庫的連接信息。所以哨兵還需要把新主庫的信息告訴客戶端。
還是可以依賴pub/sub機制來完成哨兵和客戶端的信息同步。
基於pub/sub機制的客戶端事件通知
本質上,哨兵就是一個運行在特定模式下的Redis實例,只完成監控、選主和通知的任務,因此它也具有pub/sub功能。下面是哨兵提供的一些重要的頻道。
知道這些頻道後,可以讓客戶端從哨兵這裡訂閱消息了。
由哪個哨兵執行主從切換?
在哨兵集群模式下,通過「投標仲裁」,選出哨兵Leader來執行主從切換。投標仲裁的流程如下圖所示:
任何一個實例判斷主庫「主觀下線」,就會給其他實例發送is-master-down-by-addr命令。
其他實例會根據自己和主庫的連接情況,做出Y或N響應。
一個哨兵獲得仲裁所需的贊同票數後,就可以標記主庫為「客觀下線」。這個票數可以通過哨兵配置文件中的quorum配置項來設定。
再給其他哨兵發送命令,表示希望由自己來執行主從切換,並讓其他哨兵進行投票。這個也叫「Leader選舉」,滿足以下兩個條件才能當Leader:
- 拿到半數以上的票數
- 拿到的票數>=quorum
小結
- 哨兵機制是實現Redis不間斷服務的保證。
- 哨兵機制的三大任務:監控、選主、通知。
- 為了降低誤判率,通過採用哨兵集群,並採用「少數服從多數」的原則,判斷主庫是否客觀下線。
- 哨兵集群的關鍵機制,包括:
- 基於pub/sub機制的哨兵集群組成過程;
- 基於INFO命令的從庫列表,這可以幫助哨兵和從庫建立連接;
- 基於哨兵自身的pub/sub功能,實現了客戶端的哨兵之間的事件通知。
- 哨兵集群在判斷了主庫「客觀下線」後,經過投票仲裁,選舉一個Leader來負責主從切換。
最後再分分享一個經驗:要保證所有哨兵實例的配置是一致的,尤其是主觀下線的判斷值down-after-milliseconds。