Redis 的 Sentinel 系統(tǒng)

介紹 Redis 的 Sentinel 系統(tǒng)

技術是為了解決問題而生的,Redis 的 Sentinel 系統(tǒng)實現(xiàn)了 Redis 主從服務器的自動切換。

Sentinel 是 Redis 的高可用性解決方案:由一個或多個 Sentinel 實例組成的 Sentinel 系統(tǒng)可以監(jiān)視任意多個主服務器,以及這些主服務器屬下的所有從服務器,并在被監(jiān)視的主服務器進入下線狀態(tài)時,自動將下線主服務器屬下的某個從服務器升級為新的主服務器,然后由新的主服務器代替已下線的主服務器繼續(xù)處理命令請求。

Sentinel 系統(tǒng)監(jiān)視服務器的原理

Sentinel 和一般 Redis 服務器的區(qū)別:Sentinel 本質(zhì)上只是一個運行在特殊模式下的 Redis 服務器。

Sentinel 會讀入用戶指定的配置文件,為每個要被監(jiān)視的主服務器創(chuàng)建相應的實例結構,并創(chuàng)建連向主服務器的命令連接和訂閱連接:

  • 命令連接用于,向主服務器發(fā)送命令請求;
  • 訂閱連接用于,接收指定頻道的消息。

Sentinel 通過向主服務器發(fā)送 info 命令來獲得主服務器屬下所有從服務器的地址信息,并為這些從服務器創(chuàng)建相應的實例結構,以及連向這些從服務器的命令連接和訂閱連接。


在一般情況下,Sentinel 以每十秒一次的頻率向被監(jiān)視的主服務器和從服務器發(fā)送 info 命令。當主服務器處于下線狀態(tài),或者 Sentinel 正在對主服務器進行故障轉(zhuǎn)移操作時,Sentinel 向從服務器發(fā)送 info 命令的頻率會改為每秒一次。

對于監(jiān)視同一個主服務器和從服務器的多個 Sentinel 來說,它們會以每兩秒一次的頻率,通過向被監(jiān)視服務器的 __ sentinel __:hello 頻道發(fā)送消息來向其他 Sentinel 宣告自己的存在。

每個 Sentinel 也會從 __ sentinel __:hello 頻道中接收其他 Sentinel 發(fā)來的信息,并根據(jù)這些信息為其他 Sentinel 創(chuàng)建相應的實例結構,以及命令連接。

Sentinel 只會與主服務器和從服務器創(chuàng)建命令連接和訂閱連接, Sentinel 與 Sentinel 之間則只創(chuàng)建命令連接。

Sentinel 系統(tǒng)對主服務器執(zhí)行故障轉(zhuǎn)移的整個過程

在默認情況下,Sentinel 會以每秒一次的頻率向所有與它創(chuàng)建了命令連接的實例(包括主服務器、從服務器、其他 Sentinel 在內(nèi))發(fā)送 ping 命令,并根據(jù)實例對 ping 命令的回復來判斷實例是否在線,當一個實例在指定的時長中連續(xù)向 Sentinel 發(fā)送無效回復時,Sentinel 會將這個實例判斷為主觀下線。

當 Sentinel 將一個主服務器判斷為主觀下線時,它會向同樣監(jiān)視這個主服務器的其他 Sentinel 進行詢問,看它們是否同意這個主服務器已經(jīng)進入主觀下線狀態(tài)。當 Sentinel 收集到足夠多的主觀下線投票之后,它會將主服務器判斷為客觀下線。

當一個主服務器被判斷為客觀下線時,監(jiān)視這個下線主服務器的各個 Sentinel 會進行協(xié)商,選舉出一個領頭 Sentinel,并由領頭 Sentinel 對下線主服務器執(zhí)行故障轉(zhuǎn)移操作。


在選舉產(chǎn)生出領頭 Sentinel 之后,領頭 Sentinel 將對已下線的主服務器執(zhí)行故障轉(zhuǎn)移操作,該操作包含以下三個步驟:

  1. 選出新的主服務器:在已下線主服務器屬下的所有從服務器里面,挑選出一個從服務器,并將其轉(zhuǎn)換為主服務器。
  2. 修改從服務器的復制目標:讓已下線主服務器屬下的所有從服務器改為復制新的主服務器。
  3. 將舊的主服務器變?yōu)閺姆掌?/strong>:將已下線主服務器設置為新的主服務器的從服務器,當這個舊的主服務器重新上線時,它就會成為新的主服務器的從服務器。

1、檢測服務器的下線狀態(tài)

在默認情況下,Sentinel 會以每秒一次的頻率向所有與它創(chuàng)建了命令連接的實例(包括主服務器、從服務器、其他 Sentinel 在內(nèi))發(fā)送 ping 命令,并根據(jù)實例對 ping 命令的回復來判斷實例是否在線,當一個實例在指定的時長中連續(xù)向 Sentinel 發(fā)送無效回復時,Sentinel 會將這個實例判斷為主觀下線。

當 Sentinel 將一個主服務器判斷為主觀下線時,它會向同樣監(jiān)視這個主服務器的其他 Sentinel 進行詢問,看它們是否同意這個主服務器已經(jīng)進入主觀下線狀態(tài)。

當 Sentinel 收集到足夠多的主觀下線投票之后,它會將主服務器判斷為客觀下線,并發(fā)起一次針對主服務器的故障轉(zhuǎn)移操作。


實例對 ping 命令的回復可以分為以下兩種情況:

  • 有效回復:實例返回 +pong、-loading、-masterdown 三種回復的其中一種。
  • 無效回復:實例返回除 +pong、-loading、-masterdown 三種回復之外的其他回復,或者在指定時限內(nèi)沒有返回任何回復。

1、檢測主觀下線狀態(tài)

down-after-milliseconds 選項(主觀下線時長 選項)

Sentinel 配置文件中的 down-after-milliseconds 選項指定了 Sentinel 判斷實例進入主觀下線所需的時間長度。

如果一個實例在 down-after-milliseconds 毫秒內(nèi),連續(xù)向 Sentinel 返回無效回復,那么 Sentinel 就會將該實例標記為主觀下線。修改這個實例所對應的實例結構,在結構的 flags 屬性中打開 SRI_S_DOWN 標識,以此來表示這個實例已經(jīng)進入主觀下線狀態(tài)。


down-after-milliseconds 選項的作用范圍

用戶設置的 down-after-milliseconds 選項的值,不僅會被 Sentinel 用來判斷主服務器的主觀下線狀態(tài),還會被用于判斷主服務器屬下的所有從服務器,以及所有同樣監(jiān)視這個主服務器的其他 Sentinel 的主觀下線狀態(tài)。

2、檢測客觀下線狀態(tài)

當 Sentinel 將一個主服務器判斷為主觀下線之后,為了確認這個主服務器是否真的下線了,它會向同樣監(jiān)視這個主服務器的其他 Sentinel 進行詢問,看它們是否也認為主服務器已經(jīng)進入了下線狀態(tài)(可以是主觀下線或者客觀下線)。

當 Sentinel 從其他 Sentinel 那里接收到足夠數(shù)量的已下線判斷之后,Sentinel 就會將主服務器判定為客觀下線,并對主服務器執(zhí)行故障轉(zhuǎn)移操作。

客觀下線狀態(tài)的判斷條件:當認為主服務器已經(jīng)進入下線狀態(tài)的 Sentinel 的數(shù)量,超過 Sentinel 配置中設置的 quorum 參數(shù)的值,那么該 Sentinel 就會認為主服務器已經(jīng)進入客觀下線狀態(tài)。


檢測客觀下線的具體流程:

源 Sentinel 發(fā)送命令:Sentinel 使用 sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid> 命令詢問其他 Sentinel 是否同意主服務器已下線。

目標 Sentinel 接收命令:當一個 Sentinel(目標 Sentinel)接收到另一個 Sentinel(源 Sentinel) 發(fā)來的該命令時,目標 Sentinel 會分析并取出命令請求中包含的各個參數(shù),并根據(jù)其中的主服務器 IP 和端口號,檢查主服務器是否已下線,然后向源 Sentinel 返回一條包含三個參數(shù)的 MultiBulk 回復作為該命令的回復。

  • down_state:返回目標 Sentinel 對主服務器的檢查結果,1 代表主服務器已下線,0 代表主服務器未下線
  • leader_runid:可以是 * 符號或者目標 Sentinel 的局部領頭 Sentinel 的運行 ID。*符號代表命令僅僅用于檢測主服務器的下線狀態(tài),而局部領頭 Sentinel 的運行 ID 則用于選舉領頭 Sentinel
  • leader_epoch:目標 Sentinel 的局部領頭 Sentinel 的配置紀元,用于選舉領頭 Sentinel。僅在 leader_ runid 的值不為 * 時有效,如果 leader_ runid 的值為 *,那么 leader_ epoch 總為 0

源 Sentinel 接收命令的回復:根據(jù)其他 Sentinel 對該命令的回復,Sentinel 將統(tǒng)計其他 Sentinel 同意主服務器已下線的數(shù)量,當這一數(shù)量達到配置指定的判斷客觀下線所需的數(shù)量時,Sentinel 就會將該實例標記為客觀下線。修改這個實例所對應的實例結構,在結構的 flags 屬性中打開 SRI_O_DOWN 標識,以此來表示這個實例已經(jīng)進入客觀下線狀態(tài)。


Redis 檢測客觀下線狀態(tài) / 選舉領頭 Sentinel 的命令:sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>

  • 參數(shù) ip:被 Sentinel 判斷為主觀下線的主服務器的 IP 地址
  • 參數(shù) port:被 Sentinel 判斷為主觀下線的主服務器的端口號
  • 參數(shù) current_epoch:Sentinel 當前的配置紀元,用于選舉領頭 Sentinel
  • 參數(shù) runid:該參數(shù)的值可以是 * 符號或者 Sentinel 的運行 ID。* 符號代表命令僅僅用于檢測主服務器的客觀下線狀態(tài),而 Sentinel 的運行 ID 則用于選舉領頭 Sentinel

2、選舉領頭 Sentinel

當一個主服務器被判斷為客觀下線時,監(jiān)視這個下線主服務器的各個 Sentinel 會進行協(xié)商,選舉出一個領頭 Sentinel,并由領頭 Sentinel 對下線主服務器執(zhí)行故障轉(zhuǎn)移操作。

Sentinel 系統(tǒng)選舉領頭 Sentinel 的方法是對 Raft 算法的領頭選舉方法的實現(xiàn)。

選舉領頭 Sentinel 的規(guī)則

Redis 選舉領頭 Sentinel 的規(guī)則:過半數(shù)原則

每個發(fā)現(xiàn)主服務器進入客觀下線的 Sentinel 都會要求其他 Sentinel 將自己設置為局部領頭 Sentinel。在一個配置紀元里面,所有 Sentinel 都只有一次將某個 Sentinel 設置為局部領頭 Sentinel 的機會,并且局部領頭一旦設置,在這個配置紀元里面就不能再更改。

配置紀元實際上就是一個計數(shù)器,并沒有什么特別的。

每次進行領頭 Sentinel 選舉之后,不論選舉是否成功,所有 Sentinel 的配置紀元(configuration epoch)的值都會自增一次。

Sentinel 設置局部領頭 Sentinel 的規(guī)則是先到先得:最先向目標 Sentinel 發(fā)送設置要求的源 Sentinel 將成為目標 Sentinel 的局部領頭 Sentinel,而之后接收到的所有設置要求都會被目標 Sentinel 拒絕。

  • 如果有某個 Sentinel 被半數(shù)以上的 Sentinel 設置成了局部領頭 Sentinel,那么這個 Sentinel 成為領頭 Sentinel。
  • 如果在給定時限內(nèi),沒有一個 Sentinel 被選舉為領頭 Sentinel,那么各個 Sentinel 將在一段時間之后再次進行選舉,直到選出領頭 Sentinel 為止。

因為領頭 Sentinel 的產(chǎn)生需要半數(shù)以上 Sentinel 的支持,并且每個 Sentinel 在每個配置紀元里面只能設置一次局部領頭 Sentinel,所以在一個配置紀元里面,只會出現(xiàn)一個領頭 Sentinel。

選舉領頭 Sentinel 的命令

Redis 檢測客觀下線狀態(tài) / 選舉領頭 Sentinel 的命令:sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>

  • 參數(shù) ip:被 Sentinel 判斷為主觀下線的主服務器的 IP 地址
  • 參數(shù) port:被 Sentinel 判斷為主觀下線的主服務器的端口號
  • 參數(shù) current_epoch:Sentinel 當前的配置紀元,用于選舉領頭 Sentinel
  • 參數(shù) runid:該參數(shù)的值可以是 * 符號或者 Sentinel 的運行 ID。* 符號代表命令僅僅用于檢測主服務器的客觀下線狀態(tài),而 Sentinel 的運行 ID 則用于選舉領頭 Sentinel

源 Sentinel 發(fā)送命令:當一個 Sentinel(源 Sentinel)向另一個 Sentinel(目標 Sentinel)發(fā)送該命令,并且命令中的 runid 參數(shù)不是 * 符號而是源 Sentinel 的運行 ID 時,這表示源 Sentinel 要求目標 Sentinel 將前者設置為后者的局部領頭 Sentinel。


目標 Sentinel 接收命令:目標 Sentinel 在接收到sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>之后,將向源 Sentinel 返回一條命令回復,回復中的 leader_runid 參數(shù)和 leader_epoch 參數(shù)分別記錄了目標 Sentinel 的局部領頭 Sentinel 的運行 ID 和 配置紀元。

源 Sentinel 接收命令的回復:源 Sentinel 在接收到目標 Sentinel 返回的命令回復之后,會檢查回復中 leader_epoch 參數(shù)的值和自己的配置紀元是否相同,如果相同的話,那么源 Sentinel 繼續(xù)取出回復中的 leader_runid 參數(shù),如果 leader_runid 參數(shù)的值和源 Sentinel 的運行 ID 一致,那么表示目標 Sentinel 將源 Sentinel 設置成了局部領頭 Sentinel。

3、故障轉(zhuǎn)移

在選舉產(chǎn)生出領頭 Sentinel 之后,領頭 Sentinel 將對已下線的主服務器執(zhí)行故障轉(zhuǎn)移操作,該操作包含以下三個步驟:

  1. 選出新的主服務器:在已下線主服務器屬下的所有從服務器里面,挑選出一個從服務器,并將其轉(zhuǎn)換為主服務器。
  2. 修改從服務器的復制目標:讓已下線主服務器屬下的所有從服務器改為復制新的主服務器。
  3. 將舊的主服務器變?yōu)閺姆掌?/strong>:將已下線主服務器設置為新的主服務器的從服務器,當這個舊的主服務器重新上線時,它就會成為新的主服務器的從服務器。

1、選出新的主服務器

故障轉(zhuǎn)移操作第一步要做的就是在已下線主服務器屬下的所有從服務器中,挑選出一個狀態(tài)良好、數(shù)據(jù)完整的從服務器,然后向這個從服務器發(fā)送 slaveof no one 命令,將這個從服務器轉(zhuǎn)換為主服務器。


新的主服務器是怎樣挑選出來的

領頭 Sentinel 會將已下線主服務器的所有從服務器保存到一個列表里面,然后按照以下規(guī)則,一項一項地對列表進行過濾:

  1. 根據(jù) 在線狀態(tài) 過濾:刪除列表中所有處于下線或者斷線狀態(tài)的從服務器,這可以保證列表中剩余的從服務器都是正常在線的。
  2. 根據(jù) 通信情況 過濾:刪除列表中所有最近五秒內(nèi)沒有回復過領頭 Sentinel 的 info 命令的從服務器,這可以保證列表中剩余的從服務器都是最近成功進行過通信的。
  3. 根據(jù) 斷連時間 過濾:刪除所有與已下線主服務器連接斷開超過 down-after- milliseconds * 10 毫秒的從服務器:down-after-milliseconds 選項指定了判斷主服務器下線所需的時間,而刪除斷開時長超過 down-after- milliseconds * 10 毫秒的從服務器,則可以保證列表中剩余的從服務器都沒有過早地與主服務器斷開連接,換句話說,列表中剩余的從服務器保存的數(shù)據(jù)都是比較新的。
  4. 根據(jù) 從庫的優(yōu)先級 篩選:之后,領頭 Sentinel 將根據(jù)從服務器的優(yōu)先級,對列表中剩余的從服務器進行排序,并選出其中優(yōu)先級最高的從服務器。
  5. 根據(jù) 復制偏移量 篩選:如果有多個具有相同最高優(yōu)先級的從服務器,那么領頭 Sentinel 將按照從服務器的復制偏移量,對具有相同最高優(yōu)先級的所有從服務器進行排序,并選出其中偏移量最大的從服務器(復制偏移量最大的從服務器就是保存著最新數(shù)據(jù)的從服務器)。
  6. 根據(jù) 運行 ID 篩選:最后,如果有多個優(yōu)先級最高、復制偏移量最大的從服務器,那么領頭 Sentinel 將按照運行 ID 對這些從服務器進行排序,并選出其中運行 ID 最小的從服務器。

2、修改從服務器的復制目標

當新的主服務器出現(xiàn)之后,領頭 Sentinel 下一步要做的就是,讓已下線主服務器屬下的所有從服務器去復制新的主服務器,這一動作可以通過向從服務器發(fā)送 slaveof 命令來實現(xiàn)。

3、將舊的主服務器變?yōu)閺姆掌?/h3>

故障轉(zhuǎn)移操作最后要做的是,將已下線的主服務器設置為新的主服務器的從服務器。

因為舊的主服務器已經(jīng)下線,所以這種設置是保存在服務器對應的實例結構里面的,當服務器重新上線時,Sentinel 就會向它發(fā)送 slaveof 命令,讓它成為新主服務器的從服務器。

總結 Sentinel 系統(tǒng)中的周期命令

在 Sentinel 系統(tǒng)中,有很多地方都會以一定的頻率向指定的服務器發(fā)送命令,下面對所有周期命令進行總結。

檢測下線狀態(tài)的 ping 命令:在默認情況下,Sentinel 會以每秒一次的頻率向所有與它創(chuàng)建了命令連接的實例(包括主服務器、從服務器、其他 Sentinel 在內(nèi))發(fā)送 ping 命令,并根據(jù)實例對 ping 命令的回復來判斷實例是否在線。

獲取服務器信息的 info 命令:在一般情況下,Sentinel 以每十秒一次的頻率向被監(jiān)視的主服務器和從服務器發(fā)送 info 命令,當主服務器處于下線狀態(tài),或者 Sentinel 正在對主服務器進行故障轉(zhuǎn)移操作時,Sentinel 向從服務器發(fā)送 info 命令的頻率會改為每秒一次。

參考資料

《Redis 設計與實現(xiàn)》書籍

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容