Redis高可用(一)


最近在學習 Redis 的高可用方案,就從 sentinel 開始。本篇文檔基本只是 redis sentinel 官方文檔 的摘要和總結,感興趣的直接閱讀官方文檔是更好的選擇。


基本原理

Sentinel 的原理並不複雜:

  • 啓動 n 個 sentinel 實例,這些 sentinel 實例會去監控你指定的 redis master/slaves
  • 當 redis master 節點掛掉後, Sentinel 實例通過 ping 檢測失敗發現這種情況就認爲該節點進入 SDOWN 狀態,也就是檢測的sentinel 實例主觀地(Subjectively)認爲該 redis master 節點掛掉。
  • 當一定數目(Quorum 參數設定)的 Sentinel 實例都認爲該 master 掛掉的情況下,該節點將轉換進入 ODOWN狀態,也就是客觀地(Objectively)掛掉的狀態。
  • 接下來 sentinel 實例之間發起選舉,選擇其中一個 sentinel 實例發起 failover 過程:從 slave 中選擇一臺作爲新的 master,讓其他 slave 從新的 master複製數據,並通過 Pub/Sub 發佈事件。
  • 使用者客戶端從任意 Sentinel 實例獲取 redis 配置信息,並監聽(可選)Sentinel 發出的事件: SDOWN, ODOWN 以及 failover 等,並做相應主從切換,Sentinel還扮演了服務發現的角色。
  • Sentinel 的 Leader 選舉採用的是 Raft 協議 。

一張示意圖,正常情況下:

這裏寫圖片描述
當 M1 掛掉後:
這裏寫圖片描述

節點 2 被提升爲 master,Sentinel 通知客戶端和 slaves 去使用新的 Master。


搭建實驗環境

兩個 redis,一個主一個從,分別監聽在 6379 和 6380 端口

$ redis-server
$ redis-server --port 6380

redis-cli -p 6380 連上 6380 端口的 redis,執行 slaveof 127.0.0.1 6379 將它設置爲 6379 的 slave。
啓動三個 sentinel 實例,分別監聽在 5000 – 5002 端口,並且監控 6379 的 redis master,首先是配置文件
s1.conf:

port 5000
sentinel monitor mymaster 127.0.0.1 6370 2
sentinel down-after-milliseconds mymaster 1000
sentinel failover-timeout mymaster 60000

其他兩個配置文件是 s2.conf 和 s3.conf 只是將 port 5000 修改爲 5001 和 5002,就不再重複。 需要確保配置文件是可寫的,因爲 Sentinel 會往配置文件裏添加很多信息作爲狀態持久化,這是爲了重啓等情況下可以正確地恢復 sentinel 的狀態。

啓動:

$ redis-sentinel s1.conf
$ redis-sentinel s2.conf
$ redis-sentinel s3.conf

配置說明:

  • port ,指定 sentinel 啓動後監聽的端口,sentinel 實例之間需要通過此端口通訊。
  • sentinel monitor [name] [ip] [port] [quorum] ,最重要的配置,指定要監控的 redis master 的 IP和端口,給這個監控命名 name。Quorum 指定 至少 多少個 sentinel 實例對 redis master掛掉的情況達成一致,只有達到這個數字後,Sentinel 纔會去開始一次 failover 過程。
  • down-after-milliseconds,設定 Sentinel 發現一個 redis 沒有響應 ping 到 Sentinel認爲該 redis 實例不可訪問的時間。 failover-timeout,Sentinel 實例投票對於同一個 master 發起
  • failover 過程的間隔時間,防止同時開始多次 failover。

Sentinel 啓動後會輸出類似的日誌:

17326:X 13 Oct 12:00:55.143 # +monitor master mymaster 127.0.0.1 6379 quorum 2
17326:X 13 Oct 12:00:55.143 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379

表示開始監控 mymaster 集羣,並輸出集羣的基本信息。

以及 Sentinel 之間的感知日誌,比如 s3 節點的輸出:

18441:X 13 Oct 12:01:39.985 * +sentinel sentinel **重點內容**eab05ac9fc34d8af6d59155caa195e0df5e80d73 127.0.0.1 5000 @ mymaster 127.0.0.1 6379
18441:X 13 Oct 12:01:52.918 * +sentinel sentinel 4bf24767144aea7b4d44a7253621cdd64cea6634 127.0.0.1 5002 @ mymaster 127.0.0.1 6379

查看信息

可以用 redis-cli 連上 sentinel 實例,查看信息:

$ redis-cli -p 5000
127.0.0.1:5000> sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "6379"
 7) "runid"
 8) "4b97e168125b735e034d49c7b1f45925f43aded9"
 9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "729"
19) "last-ping-reply"
20) "729"
21) "down-after-milliseconds"
22) "1000"
23) "info-refresh"
24) "6258"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "11853370"
29) "config-epoch"
30) "0"
31) "num-slaves"
32) "1"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "60000"
39) "parallel-syncs"
40) "1"

sentinel master [name] 用於查看監控的某個 redis master 信息,包括配置和狀態等,其他命令還包括:

  • sentinel masters 查看所有監控的 master 信息。
  • sentinel slaves [name] 查看監控的某個redis 集羣的所有 slave 節點信息。
  • sentinel sentinels [name] 查看所有 sentinel 實例信息。

更重要的一個命令是根據名稱來查詢 redis 信息,客戶端會用到:

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"

測試 Failover

我們讓 6379 的 master 主動休眠 30 秒來觀察 failover 過程:

$ redis-cli -p 6379 DEBUG sleep 30

我們可以看到每個 sentinel 進程都監控到 master 掛掉,從 sdown 狀態進入 odown,然後選舉了一個 leader 來進行 failover,最終 6380 成爲新的 master, sentinel 的日誌輸出:

18441:X 13 Oct 15:26:51.735 # +sdown master mymaster 127.0.0.1 6379
18441:X 13 Oct 15:26:51.899 # +new-epoch 1
18441:X 13 Oct 15:26:51.900 # +vote-for-leader eab05ac9fc34d8af6d59155caa195e0df5e80d73 1
18441:X 13 Oct 15:26:52.854 # +odown master mymaster 127.0.0.1 6379 #quorum 3/2
18441:X 13 Oct 15:26:52.854 # Next failover delay: I will not start a failover before Thu Oct 13 15:28:52 2016
18441:X 13 Oct 15:26:53.034 # +config-update-from sentinel eab05ac9fc34d8af6d59155caa195e0df5e80d73 127.0.0.1 5000 @ mymaster 127.0.0.1 6379
18441:X 13 Oct 15:26:53.034 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
18441:X 13 Oct 15:26:53.034 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
18441:X 13 Oct 15:26:54.045 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
18441:X 13 Oct 15:27:20.383 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380

日誌的幾個主要事件:

  • +sdown master mymaster 127.0.0.1 6379 ,發現 master 檢測失敗,主觀認爲該節點掛掉,進入 sdown 狀態。
  • +odown master mymaster 127.0.0.1 6379 #quorum 3/2 ,有兩個 sentinel 節點認爲 master 6379 掛掉,達到配置的 quorum 值 2,因此認爲 master 已經客觀掛掉,進入 odown 狀態。
  • +vote-for-leader eab05ac9fc34d8af6d59155caa195e0df5e80d73 準備選舉一個 sentinel leader 來開始 failover。
  • +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380 切換 master 節點, failover 完成。
  • +config-update-from sentinel eab05ac9fc34d8af6d59155caa195e0df5e80d73 127.0.0.1 5000 @ mymaster 127.0.0.1 6379 更新 sentinel 配置。
  • 6379 休眠回來,作爲 slave 掛載到 6380 後面,可見 sentinel 確實同時在監控 slave 狀態,並且掛掉的節點不會自動移除,而是繼續監控。
    此時查看 sentinel 配置文件,會發現增加了一些內容:
# Generated by CONFIG REWRITE*
dir "/Users/dennis/opensources/redis-sentinel"
sentinel failover-timeout mymaster 60000
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
sentinel known-slave mymaster 127.0.0.1 6379
sentinel known-sentinel mymaster 127.0.0.1 5001 8ba1e75cbf4c268be4a2950ee7389df746c6b0b4
sentinel known-sentinel mymaster 127.0.0.1 5002 4bf24767144aea7b4d44a7253621cdd64cea6634
sentinel current-epoch 1

可以看到 sentinel 將最新的集羣狀態寫入了配置文件。


運維

命令

除了上面提到的一些查看信息的命令之外, sentinel 還支持下列命令來管理和檢測 sentinel 配置:

  • SENTINEL reset <pattern> 強制重設所有監控的 master 狀態,清除已知的 slave 和 sentinel 實例信息,重新獲取並生成配置文件。
  • SENTINEL failover <master name> 強制發起一次某個 master 的 failover,如果該 master 不可訪問的話。
  • SENTINEL ckquorum <master name> 檢測 sentinel 配置是否合理, failover 的條件是否可能滿足,主要用來檢測你的 sentinel 配置是否正常。
  • SENTINEL flushconfig 強制 sentinel 重寫所有配置信息到配置文件。
    增加和移除監控以及修改配置參數:
SENTINEL MONITOR <name> <ip> <port> <quorum>
SENTINEL REMOVE <name>
SENTINEL SET <name> <option> <value>

增加和移除 Sentinel

增加新的 Sentinel 實例非常簡單,修改好配置文件,啓動即可,其他 Sentinel 會自動發現該實例並加入集羣。如果要批量啓動一批 Sentinel 節點,最好以 30 秒的間隔一個一個啓動爲好,這樣能確保整個 Sentinel 集羣的大多數能夠及時感知到新節點,滿足當時可能發生的選舉條件。

移除一個 sentinel 實例會相對麻煩一些,因爲 sentinel 不會忘記已經感知到的 sentinel 實例,所以最好按照下列步驟來處理:

  • 停止將要移除的 sentinel 進程。
  • 給其餘的 sentinel 進程發送 SENTINEL RESET *命令來重置狀態,忘記將要移除的 sentinel,每個進程之間間隔 30 秒。
  • 確保所有 sentinel 對於當前存貨的sentinel 數量達成一致,可以通過 SENTINEL MASTER [mastername] 命令來觀察,或者查看配置文件

客戶端實現

客戶端從過去直接連接 redis ,變成:

  1. 先連接一個 sentinel 實例
  2. 使用 SENTINEL get-master-addr-by-name master-name 獲取 redis 地址信息。連接返回的 redis 地址信息,通過 ROLE 命令查詢是否是 master。如果是,連接進入正常的服務環節。否則應該斷開重新查詢。
  3. (可選)客戶端可以通過 SENTINEL sentinels [name] 來更新自己的 sentinel 實例列表。
  4. 當 Sentinel 發起 failover 後,切換了新的 master,sentinel 會發送 CLIENT KILL TYPE normal 命令給客戶端,客戶端需要主動斷開對老的master 的鏈接,然後重新查詢新的 master 地址,再重複走上面的流程。這樣的方式仍然相對不夠實時,可以通過 sentinel 提供的 Pub/Sub 來更快地監聽到 failover 事件,加快重連。

如果需要實現讀寫分離,讀走 slave,那可以走 SENTINEL slaves [name] 來查詢 slave 列表並連接。


生產環境推薦

對於一個最小集羣,Redis 應該是一個 master 帶上兩個 slave,並且開啓下列選項:

min-slaves-to-write 1
min-slaves-max-lag 10

這樣能保證寫入 master 的同時至少寫入一個 slave,如果出現網絡分區阻隔併發生 failover 的時候,可以保證寫入的數據最終一致而不是丟失,寫入老的 master 會直接失敗,參考 Consistency under partitions 。

Slave 可以適當設置優先級,除了 0 之外(0 表示永遠不提升爲 master),越小的優先級,越有可能被提示爲 master。如果 slave 分佈在多個機房,可以考慮將和 master 同一個機房的 slave 的優先級設置的更低以提升他被選爲新的 master 的可能性。

考慮到可用性和選舉的需要,Sentinel 進程至少爲 3 個,推薦爲 5 個,如果有網絡分區,應當適當分佈(比如 2 個在 A 機房, 2 個在 B 機房,一個在 C 機房)等。


其他

由於 Redis 是異步複製,所以 sentinel 其實無法達到強一致性,它承諾的是最終一致性:最後一次 failover 的 redis master 贏者通吃,其他slave 的數據將被丟棄,重新從新的 master 複製數據。此外還有前面提到的分區帶來的一致性問題。

其次,Sentinel 的選舉算法依賴時間,因此要確保所有機器的時間同步,如果發現時間不一致,Sentinel 實現了一個 TITL 模式來保護系統的可用性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章