最近在學習 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 ,變成:
- 先連接一個 sentinel 實例
- 使用
SENTINEL get-master-addr-by-name master-name
獲取 redis 地址信息。 - 連接返回的 redis 地址信息,通過
ROLE
命令查詢是否是 master。如果是,連接進入正常的服務環節。否則應該斷開重新查詢。 - (可選)客戶端可以通過
SENTINEL sentinels [name]
來更新自己的 sentinel 實例列表。
當 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 模式來保護系統的可用性。