鍍金池/ 教程/ 大數(shù)據(jù)/ 高可用(下)
使用 Redis 實現(xiàn) Twitter(上)
集群(下)
使用 Redis 實現(xiàn) Twitter(下)
使用 Redis 作為 LRU 緩存
高可用(上)
高可用客戶端指引
集群(中)
高可用(下)
持久化
Redis 介紹
集中插入
集群(上)
從入門到精通(上)
從入門到精通(下)
從入門到精通(中)
分片
數(shù)據(jù)類型初探
復制

高可用(下)

分割下的一致性(Consistency under partitions)

Redis Sentinel 的配置是最終一致性的,所以每個分區(qū)會被統(tǒng)一到一個可用的更高版本的配置。但是,在使用 Sentinel 的真實世界系統(tǒng)中有三個不同的角色:

  • Redis 實例。
  • Sentinel 實例。
  • 客戶端。

為了定義系統(tǒng)的行為,我們得考慮這三個角色。

下面是一個有三個節(jié)點的簡單網(wǎng)絡,每一個節(jié)點運行一個 Redis 實例和一個 Sentinel 實例:

http://wiki.jikexueyuan.com/project/redis-guide/images/1.png" alt="" />

在這個系統(tǒng)中,初始狀態(tài)是 Redis 3 是主服務器,Redis 1 和 Redis 2 是從服務器。分割發(fā)生了,隔斷了老的主服務器。Sentinel 1 和 2 開始故障轉(zhuǎn)移,提升 Sentinel 1 作為新的主服務器。

Sentinel 的屬性保證,Sentinel 1 和 2 現(xiàn)在擁有主服務器的最新配置。但是,Sentinel 3 仍是舊的配置,因為它存在于一個不同的分割中。

當網(wǎng)絡分割恢復正常了,Sentinel 3 將會更新其配置,但是,如果有客戶端與老的主服務器被分割在一起,在分割期間會發(fā)生什么事情呢?

客戶端會繼續(xù)向 Redis 3 寫,即老的主服務器。當分割又聚合在一起,Redis 3 將會變成 Redis 1 的從服務器,分割期間所有寫入的數(shù)據(jù)會丟失。

你可能想或者不想這種場景發(fā)生,取決于你的配置:

  • 如果你將 Redis 用作緩存,客戶端 B 可以繼續(xù)往老的主服務器寫,即使這些數(shù)據(jù)會丟失。
  • 如果你將 Redis 用作存儲,這樣就不好了,你需要來配置系統(tǒng)以部分地阻止問題的發(fā)生。

因為 Redis 是異步復制,這種場景下沒有完全阻止數(shù)據(jù)丟失的辦法,但是你可以使用下面的 Redis 配置選項,來限制 Redis 3 和 Redis 1 之間的分歧:

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

有了上面的配置(請查看 Redis 分發(fā)版本中自帶的 redis.conf 文件中的注釋獲取更多的信息),扮演主服務器的 Redis 實例如果不能寫入到至少一個從服務器,將會停止接受寫請求。由于復制是異步的,不能寫入的意思就是從服務器也是斷開的,或者在指定的 max-lag 秒數(shù)沒有發(fā)送異步回應 (acknowledges)。

使用這個配置,上面例子中的 Redis 3 在 10 秒鐘之后變得不可用。當分割恢復了,Sentinel 3 的配置將會統(tǒng)一為新的,客戶端 B 可以獲取合法的配置并且繼續(xù)。

Sentinel 的持久化狀態(tài) (Sentinel persistent state)

Sentinel 的狀態(tài)被持久化在 Sentinel 的配置文件中。例如,每次創(chuàng)建(領導者 leader Sentinel)或者收到新的配置,主服務器會將配置連同配置紀元持久化到磁盤中。這意味著,停止和重啟 Sentinel 進程是安全的。

Sentinel 重配置實例(Sentinel reconfiguration of instances)

即使沒有故障轉(zhuǎn)移在進行中,Sentinel 也會一直嘗試在被監(jiān)控的實例上設置當前配置。尤其是:

  • 聲稱要成為主服務器的從服務器(根據(jù)當前配置),會被配置為從服務器來復制當前主服務器。
  • 連接到錯誤主服務器的從服務器,會被重新配置來復制正確的主服務器。
  • 為了 Sentinel 重新配置從服務器,錯誤的配置必須要觀察一段時間,一段大于用于廣播新配置所使用的時間。

這防止了持有舊配置(例如,因為剛剛從分割中恢復)的 Sentinel 會嘗試在收到變更之前改變從服務器的配置。

也要注意,一直嘗試使用當前配置使得故障轉(zhuǎn)移對分割具有更強的抵抗力的語義是什么:

  • 被故障轉(zhuǎn)移的主服務器當再次可用時被重新配置成從服務器。
  • 被分割的從服務器在一旦可到達時被重新配置。

從服務器的選舉和優(yōu)先級(Slave selection and priority)

當 Sentinel 實例準備執(zhí)行故障轉(zhuǎn)移,也就是當主服務器處于 ODOWN 狀態(tài),并且 Sentinel 從大多數(shù)已知 Sentinel 實例收到了故障轉(zhuǎn)移授權,需要選擇一個合適的從服務器。

從服務器的選擇過程評估從服務器的以下信息:

  1. 從主服務器斷開的時間。
  2. 從服務器的優(yōu)先級。
  3. 已處理的復制偏移量。
  4. 運行 ID。

一個從服務器被發(fā)現(xiàn)從主服務器斷開超過十倍于配置的主服務器超時(down-after-milliseconds 選項),加上從正在執(zhí)行故障轉(zhuǎn)移的 Sentinel 的角度來看主服務器也不可用的時間,將會被認為不適合用于故障轉(zhuǎn)移并跳過。

更嚴謹?shù)卣f,一個從服務器的 INFO 輸出表明已從主服務器斷開超過:

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state  

就被認為不可靠并且被拋棄。

從服務器選擇只考慮通過了上面測試的從服務器,并且基于上面的標準排序,使用下面的順序。

  1. 從服務器按照 Redis 實例的 redis.conf 文件中配置的 slave-priority 排序。更低的優(yōu)先級更偏愛。
  2. 如果優(yōu)先級相同,將檢查已處理的復制偏移量,從主服務器收到更多數(shù)據(jù)的從服務器將被選擇。
  3. 如果多個從服務器有相同的優(yōu)先家,并且從主服務器處理完相同的數(shù)據(jù),將執(zhí)行進一步的檢查,選擇按照字典順序具有更小運行 ID 的從服務器。擁有較小的運行 ID 對從服務器并不是一個真正的優(yōu)勢,但是有助于從服務器選舉過程更具有確定性,而不是隨機選擇一個。

如果對機器有強烈的偏好的話,Redis 主服務器(故障轉(zhuǎn)移以后成為從服務器)和從服務器都需要配置 slave-priority。否則,所有的實例都可以使用默認的運行 ID 來運行(這是建議的設置,因為按照復制偏移量來選擇從服務器要有趣得多)。

Redis 實例可以配置一個特殊的 slave-priority 值 0,這樣就一定不會被 Sentinel 選舉為新的主服務器。但是,按照這樣配置的從服務器仍然會被 Sentinel 重新配置,從而在故障轉(zhuǎn)移后復制新的主服務器,唯一的區(qū)別是永遠不會變成主服務器。

Sentinel 和 Redis 身份驗證(authentication)

當主服務器被配置為需要客戶端傳遞密碼時,作為安全措施,從服務器也需要知道這個密碼來驗證主服務器,并且創(chuàng)建用于異步復制協(xié)議的主從連接。

使用下面的配置指令完成:

主服務器中的 requirepass 用來設置密碼驗證,以確保實例不會處理沒有驗證過的客戶端的請求。 從服務器中的 masterauth 用于從服務器驗證主服務器,以正確的從其復制數(shù)據(jù)。

當使用 Sentinel 就沒有單一的主服務器,因為故障轉(zhuǎn)移以后從服務器可以扮演主服務器的角色,老的主服務器被重新配置以扮演從服務器,所以你要做的就是在你所有的主服務器和從服務器實例中設置以上指令。

這通常是一種邏輯上健全的設置,因為你不想只是保護主服務器中的數(shù)據(jù),從服務器中也應擁有同樣可訪問的數(shù)據(jù)。

但是,在一些不常見的情況下,你需要從服務器無需驗證就能訪問,你仍可以通過設置從服務器的優(yōu)先級為 0(這將不允許從服務器被提升為主服務器),只為從服務器配置 masterauth 指令,不配置 requirepass 指令這樣來做到,這樣數(shù)據(jù)就可以讓未經(jīng)驗證的客戶端讀取。

Sentinel API

Sentinel 運行默認使用 TCP 端口 26379(注意,6379 是正常的 Redis 端口)。Sentinel 接受使用 Redis 協(xié)議的命令,所以你可以使用 redis-cli 或者任何其他未修改的 Redis 客戶端與 Sentinel 對話。

有兩種方式與 Sentinel 對話:可以直接查詢它來檢查被監(jiān)控的 Redis 實例的狀態(tài),看看它知道的其他 Sentinel,等等。

另外一種方式是使用發(fā)布訂閱,每當某個事件發(fā)生時,例如故障轉(zhuǎn)移,或者一個實例進入到了一個錯誤條件,等等,接收從 Sentinel 推過來的通知。

Sentinel 命令

下面是可接受的命令清單:

  • PING:這個命令僅僅返回 PONG。
  • SENTINEL masters:展示被監(jiān)控的主服務器列表及其狀態(tài)。
  • SENTINEL master <master name>:展示指定主服務器的狀態(tài)和信息。
  • SENTINEL slaves <master name>:展示指定主服務器的從服務器列表及其狀態(tài)。
  • SENTINEL get-master-addr-by-name <master name>:根據(jù)名字返回主服務器的 IP 地址和端口號。如果這臺主服務器正在故障轉(zhuǎn)移過程中或者成功結束了,返回被提升的從服務器的 IP 地址和端口。
  • SENTINEL reset <pattern>:這個命令根據(jù)匹配的名字重置所有主服務器。pattern 參數(shù)是通配符風格(glob-style)。重置進程清除主服務器的任何先前狀態(tài)(包括進行中的故障轉(zhuǎn)移),移除每一個主服務器上被發(fā)現(xiàn)和關聯(lián)的從服務器和 Sentinel。
  • SENTINEL failover <master name> 當主服務器不可達時強制故障轉(zhuǎn)移,無需要求其他的
  • Sentinel 同意(但是會發(fā)布一個新的配置版本,這樣其他 Sentinel 就會更新它們的配置)。

運行時重配置 Sentinel(Reconfiguring Sentinel)

從 Redis 2.8.4 開始,Sentinel 提供了用于添加,刪除和改變指定主服務器配置的 API。注意,如果你有多個 Sentinel 實例,你得將改變應用到所有的 Redis Sentinel 實例才能運轉(zhuǎn)正常。也就是說,改變一個 Sentinel 的配置不會自動傳播到網(wǎng)絡中的其它 Sentinel。

下面是 SENTINEL 的子命令清單,用于更新 Sentinel 實例的配置。

  • SENTINEL MONITOR <name> <ip> <port> <quorum>:這個命令告訴 Sentinel 開始監(jiān)控一個指定名字,IP 地址,端口和仲裁人數(shù)的新主服務器。這等同于 sentinel.conf 配置文件中的 sentinel monitor 配置指令,不同之處在于此處不能使用主機名作為 IP 地址,你需要提供一個 IPv4 或者 Ipv6 地址。
  • SENTINEL REMOVE <name>:用于刪除指定主服務器:主服務器不再被監(jiān)控,完全從 Sentinel 內(nèi)部狀態(tài)中移除,所以不會被 SENTINEL masters 列出,等等。
  • SENTINEL SET <name> <option> <value>:命令 SET 非常類似于 Redis 的 CONFIG SET 命令,用于改變指定主服務器的配置參數(shù)??梢灾付ǘ鄠€選項 - 值對(或者根本啥都沒有)。所有可以通過 sentinel.conf 配置的配置參數(shù)都可以通過 SET 命令配置。

下面是 SENTINEL SET 命令的一個例子,用于修改一個名為 objects-cache 的主服務器的 down-after-milliseconds 配置:

SENTINEL SET objects-cache-master down-after-milliseconds 1000  

啟動以后,SENTINEL SET 能用于設置所有在啟動配置文件中可設置的配置參數(shù)。此外,還可以僅僅只改變主服務器的仲裁人數(shù)配置,而不需要使用 SENTINEL REMOVE 和 SENTINEL MONITOR 來刪除和重新添加主服務器,而只需要:

SENTINEL SET objects-cache-master quorum 5  

注意,沒有與之等價的 GET 命令,因為 SENTINEL MASTER 以一種易于解析的格式(作為一個字段 - 值對數(shù)組)提供了所有的配置參數(shù)。

添加和刪除 Sentinel(Adding or removing Sentinels)

因為 Sentinel 實現(xiàn)的自動發(fā)現(xiàn)機制,添加一個新的 Sentinel 到你的部署中是一個很簡單的過程。所有你需要干的就是啟動一個配置用于監(jiān)控當前活躍主服務器的 Sentinel。在 10 秒鐘之內(nèi),Sentinel 就會獲得其他 Sentinel 的列表以及連接到主服務器的從服務器集合。

如果你想一次添加多個新的 Sentinel,建議一個一個的添加,等待所有其他的 Sentinel 知道了第一個再添加另一個。這在當添加新 Sentinel 的過程中發(fā)生錯誤時,仍然保證在分割的一側能達到大多數(shù)時很有用。

在沒有網(wǎng)絡分割時,這可以通過添加每個新的 Sentinel 時帶 30 秒的延遲來輕易實現(xiàn)。

在最后,可以使用命令 SENTINEL MASTER mastername 來檢查是否所有的 Sentinel 就監(jiān)控主服務器的 Sentinel 數(shù)量達成一致。

刪除一個 Sentinel 要稍微復雜一些:Sentinel 永遠不會忘記已經(jīng)發(fā)現(xiàn)的 Sentinel,即使他們在很長一段時間內(nèi)都不可達,因為我們不想動態(tài)改變用于授權故障轉(zhuǎn)移所需要的大多數(shù)以及創(chuàng)建新的配置版本。所以在沒有網(wǎng)絡分割情況下,需要執(zhí)行下面的步驟來刪除 Sentinel:

  1. 停止你想刪除的 Sentinel 的進程。
  2. 發(fā)送 SENTINEL RESET 命令到所有其他的 Sentinel 實例(如果你想重置單個主服務器可以使用精確的主服務器名來代替 )。一個一個的來,前后等待至少 30 秒。
  3. 通過檢查每個 SENTINEL MASTER mastername 的輸出,來檢查所有的 Sentinel 就當前活躍的 Sentinel 數(shù)量達成一致。

刪除舊的主服務器或不可達從服務器(unreachable)

Sentinel 不會忘記主服務器的從服務器,即使在很長時間內(nèi)都不可達。這很有用,因為這樣 Sentinel 能夠在網(wǎng)絡分割或者錯誤事件恢復后正確地重新配置一個返回的從服務器。

此外,故障轉(zhuǎn)移之后,被故障轉(zhuǎn)移的主服務器事實上被添加為新主服務器的從服務器,這樣一旦恢復重新可用,就會被重新配置來復制新的主服務器。

但是,有時候你想從 Sentinel 監(jiān)控的從服務器列表中永久刪除一個從服務器(可能是舊的主服務器)。

要做到這個,你需要發(fā)送 SENTINEL RESET mastername 命令到所有的 Sentinel:在接下來的 10 秒內(nèi),他們會刷新從服務器列表,只添加當前主服務器 INFO 輸出中的正確復制的清單。

發(fā)布和訂閱消息(Pub/Sub Messages)

客戶端可以將 Sentinel 作為一個 Redis 兼容的發(fā)布訂閱服務器(但是你不能使用 PUBLISH)來使用,來訂閱或者發(fā)布到頻道,獲取指定事件通知。

頻道名稱與事件名稱是一樣的。例如,名為 + sdown 的頻道會收到所有關于實例進入 SDOWN 條件的通知。

簡單使用 PSUBSCRIBE * 訂閱來獲得所有的消息。

下面是頻道的清單,以及使用這個 API 你會收到的消息格式。第一個單詞是頻道/事件名稱,剩下的是數(shù)據(jù)的格式。

注意:指定 instance details 的地方表示提供了下面用于表示目標實例的參數(shù):

<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>  

標識主服務器的部分 (從 @參數(shù)到結束) 是可選的,只在實例不是主服務器本身時指定。

  • +reset-master <instance details>:主服務器被重置。
  • +slave <instance details>:一個新的從服務器被發(fā)現(xiàn)和關聯(lián)。
  • +failover-state-reconf-slaves <instance details>:故障轉(zhuǎn)移狀態(tài)切換為 reconf-slaves 狀態(tài)。
  • +failover-detected <instance details>:另一個 Sentinel 啟動了故障轉(zhuǎn)移,或者任何其它外部實體被發(fā)現(xiàn)(關聯(lián)的從服務器變?yōu)橹鞣掌?。
  • +slave-reconf-sent <instance details>:領導者 Sentinel 發(fā)送了 SLAVEOF 命令到這個實例,重新配置為新的從服務器。
  • +slave-reconf-inprog <instance details>:從服務器正在重新配置為新的主服務器的從服務器,但是同步過程尚未完成。
  • +slave-reconf-done <instance details>:從服務器完成了與新主服務器的同步。
  • -dup-sentinel <instance details>:由于重復,指定主服務器的一個或多個 Sentinel 被移除。
  • +sentinel <instance details>:這個主服務器的新的 Sentinel 被發(fā)現(xiàn)和關聯(lián)。
  • +sdown <instance details>:指定的實例處于主觀下線狀態(tài)。
  • -sdown <instance details>:指定的實例不再處于主觀下線狀態(tài)。
  • +odown <instance details>:指定的實例處于客觀下線狀態(tài)。
  • -odown <instance details>:指定的實例不再處于客觀下線狀態(tài)。
  • +new-epoch <instance details>:當前紀元被更新。
  • +try-failover <instance details>:新的故障轉(zhuǎn)移進行中,等待被大多數(shù)選中。
  • +elected-leader <instance details>:贏得指定紀元的選舉,可以進行故障轉(zhuǎn)移。
  • +failover-state-select-slave <instance details>:新的故障轉(zhuǎn)移狀態(tài)是 select-slave:正在尋找合適的從服務器來提升。
  • no-good-slave <instance details>:沒有合適的從服務器來提升。一段時間后會重試,或者干脆放棄故障轉(zhuǎn)移。
  • selected-slave <instance details>:找到合適的從服務器來提升。
  • failover-state-send-slaveof-noone <instance details>:正在重新配置將提升的從服務器為主服務器,等待完成后切換。
  • failover-end-for-timeout <instance details>:故障轉(zhuǎn)移由于超時而終止, 無論如何從服務器最終被配置為復制新的主服務器。
  • failover-end <instance details>:故障轉(zhuǎn)移順利完成。所有從服務器被重配置為復制新主服務器。
  • switch-master <master name> <oldip> <oldport> <newip> <newport>:配置變更后主服務器的 IP 和地址都是指定的。這是大多數(shù)外部用戶感興趣的消息。
  • +tilt:進入 tilt 模式。
  • -tilt:退出 tilt 模式。

TILT 模式

Redis Sentinel 嚴重依賴于計算機時間:例如,為了了解一個實例是否可用,Sentinel 會記住最近成功回復 PING 命令的時間,與當前時間對比來了解這有多久。

但是,如果計算機時間以不可預知的方式改變了,或者計算機非常繁忙,或者某些原因進程阻塞了,Sentinel 可能會開始表現(xiàn)得不可預知。

TILT 模式是一個特別的保護模式,當發(fā)現(xiàn)一些會降低系統(tǒng)可靠性的奇怪問題時,Sentinel 就會進入這種模式。Sentinel 的定時中斷通常每秒鐘執(zhí)行 10 次,所以我們期待兩次定時中斷調(diào)用之間相隔 100 毫秒左右。

Sentinel 做的就是記錄上一次定時中斷調(diào)用的時間,與當前調(diào)用進行比較:如果時間差是負數(shù)或者出乎意料的大(2 秒或更多),就進入了 TILT 模式(或者如果已經(jīng)進入了,退出 TILT 模式將被推遲)。

當處于 TILT 模式時,Sentinel 會繼續(xù)監(jiān)控一切,但是:

  • 停止一切動作。
  • 開始回復負數(shù)給 SENTINEL is-master-down-by-addr 請求,因為檢測失敗的能力不再可信了。

如果一切表現(xiàn)正常了 30 秒,將退出 TILT 模式。

處理 - BUSY 狀態(tài)

(警告:還未實現(xiàn))

當腳本運行超過配置的腳本限制時間時返回 - BUSY 錯誤。當這種情況發(fā)生時,在觸發(fā)故障轉(zhuǎn)移之前 Redis Sentinel 會嘗試發(fā)送 SCRIPT KILL 命令,這只有在腳本是只讀的情況下才能成功。

Sentinel 客戶端實現(xiàn)

Sentinel 需要顯式的客戶端支持,除非系統(tǒng)被配置為執(zhí)行一個腳本,來實現(xiàn)透明重定向所有請求到新的主服務器實例(虛擬 IP 或其它類似系統(tǒng))??蛻舳藥鞂崿F(xiàn)的主題在 Sentinel 客戶端指引手冊中討論(請期待本系列后續(xù)文檔,譯者注)。