上一篇文章介紹了高可靠方案:主從叢集模式。通過主從庫的讀寫分離,來保證服務的可靠性。
當某個從庫出現故障時,不影響服務的使用,主庫仍然可以處理寫命令,其他從庫可以處理讀命令。但主庫發生故障,就不能處理寫命令了,從庫只能處理讀命令。這就影響服務的正常使用了,該如何解決呢?
只要找一個從庫當主庫就可以解決了。但還有三個問題需要處理:
- 主庫真的掛了嗎?
- 該選擇哪個從庫作為主庫?
- 怎麼把新主庫的相關資訊通知給從庫和客戶端?
這裡就要介紹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。