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

高可用(下)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state  

就被認(rèn)為不可靠并且被拋棄。

從服務(wù)器選擇只考慮通過了上面測試的從服務(wù)器,并且基于上面的標(biāo)準(zhǔn)排序,使用下面的順序。

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

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

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

Sentinel 和 Redis 身份驗證(authentication)

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

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

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

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

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

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

Sentinel API

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

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

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

Sentinel 命令

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

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

運(yùn)行時重配置 Sentinel(Reconfiguring Sentinel)

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

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

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

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

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

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

SENTINEL SET objects-cache-master quorum 5  

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

TILT 模式

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

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

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

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

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

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

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

處理 - BUSY 狀態(tài)

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

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

Sentinel 客戶端實現(xiàn)

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