老司機帶你玩轉面試(5):Redis 集羣模式 Redis Cluster

前文回顧

建議前面文章沒看過的同窗先看下前面的文章:html

「老司機帶你玩轉面試(1):緩存中間件 Redis 基礎知識以及數據持久化」java

「老司機帶你玩轉面試(2):Redis 過時策略以及緩存雪崩、擊穿、穿透」node

「老司機帶你玩轉面試(3):Redis 高可用之主從模式」git

「老司機帶你玩轉面試(4):Redis 高可用之哨兵模式」github

簡介

以前介紹的 Redis 的高可用方案:主從模式或者說哨兵模式,都只是在解決高可用的問題,好比說主從模式解決了讀高可用,哨兵模式解決了寫高可用。面試

若是咱們須要緩存的數據量比較少,幾個 G 足夠用了,那麼這兩種方案的高可用模式徹底能夠知足需求,一個 master 對多個 salve ,須要幾個 salve 跟讀的吞吐量相關,而後再搞一個 sentinel 集羣去保證 Redis 主從模式的高可用。redis

可是若是咱們有大量的數據須要進緩存,單機的存儲容量沒法知足的時候,怎麼辦?算法

這時,須要用到分佈式緩存,就在幾年前,想用分佈式緩存還得藉助中間件來實現,好比當時風靡一時的 codis ,或者說還有 twemproxy 。在使用上,咱們對 Redis 中間件進行讀寫操做, Redis 中間件負責將咱們的數據分佈式的存儲在多臺機器上的 Redis 實例中。緩存

而 Redis 也是不斷在發展的,終於在 3.0 的版本中(其實對如今來說也是比較早的版本了,如今的版本都 6.0 了),原生支持了集羣模式。服務器

能夠作到在多臺機器上,部署多個 Redis 實例,每一個實例存儲一部分的數據,同時每一個 Redis 主實例能夠掛載 Redis 從實例,實現了 Redis 主實例掛了,會自動切換到 Redis 從實例上來。

除了實現了分佈式存儲之外,還順便實現了高可用。

分佈式尋址算法

Redis Cluster 的數據是分佈式存儲的,這就必然會引起一個問題,假如我有 3 個節點,我如何知道一個 key 是存在這 3 個節點的哪個節點上,取數的時候如何準確的找到對應的節點將數據取出來?這就要用到分佈式尋址算法了。

常見的分佈式尋址算法有兩種:

  • hash 算法
  • 一致性 hash 算法

hash 算法

hash 算法是對 key 進行 hash 運算後取值,而後對節點的數量取模。

接着將 key 存入對應的節點,可是,一旦其中某個節點宕機,全部的請求過來,都會基於最新的存活的節點數量進行取模運算,這就會致使大多數的請求沒法拿到緩存數據。

舉個例子,一開始我有 3 個節點,這時你們正常的取模運算將數據基本均勻的存在了 3 個節點上,忽然宕機了一臺,如今只剩下 2 個有效的節點了,這時全部的運算都會基於最新的 2 個節點進行取模運算,原來的 key 對 2 進行取模運算後,顯然和對 3 進行取模運算獲得的結果是不同的。

結果是大量的數據請求會直接打在 DB 上,這是不可容忍的。

一致性 hash 算法

一致性 hash 算法將整個 hash 值空間組織成一個虛擬的圓環,整個空間按順時針方向組織,下一步將各個 master 節點(使用服務器的 ip 或主機名)進行 hash。這樣就能肯定每一個節點在其哈希環上的位置。

來看一個經典的一致性 hash 算法的環狀示意圖:

來了一個 key,首先計算 hash 值,並肯定此數據在環上的位置,今後位置沿環順時針「行走」,遇到的第一個 master 節點就是 key 所在位置。

在一致性哈希算法中,若是一個節點掛了,受影響的數據僅僅是此節點到環空間前一個節點(沿着逆時針方向行走遇到的第一個節點)之間的數據,其它不受影響。增長一個節點也同理。

一致性哈希算法在節點太少時,容易由於節點分佈不均勻而形成緩存熱點的問題。爲了解決這種熱點問題,一致性 hash 算法引入了虛擬節點機制,即對每個節點計算多個 hash,每一個計算結果位置都放置一個虛擬節點。這樣就實現了數據的均勻分佈,負載均衡。

Redis Cluster 的 hash slot 算法

Redis Cluster 的實現方案十分的聰明,它的分區方式採用了虛擬槽分區。

Redis Cluster 首先會預設虛擬槽,每一個槽就至關於一個數字,有必定範圍,每一個槽映射一個數據子集。

Redis Cluster中預設虛擬槽的範圍爲 0 到 16383

  1. Redis Cluster 會把 16384 個槽按照節點數量進行平均分配,由節點進行管理。
  2. 當一個 key 過來的時候,會對這個 key 按照 CRC16 規則進行 hash 運算。
  3. 把 hash 結果對 16383 進行取餘。
  4. 把餘數發送給 Redis 節點。
  5. 節點接收到數據,驗證是否在本身管理的槽編號的範圍:
    1. 若是在本身管理的槽編號範圍內,則把數據保存到數據槽中,而後返回執行結果。
    2. 若是在本身管理的槽編號範圍外,則會把數據發送給正確的節點,由正確的節點來把數據保存在對應的槽中。

注意:Redis Cluster 的節點之間會共享消息,每一個節點都會知道是哪一個節點負責哪一個範圍內的數據槽

虛擬槽分佈方式中,因爲每一個節點管理一部分數據槽,數據保存到數據槽中。當節點擴容或者縮容時,對數據槽進行從新分配遷移便可,數據不會丟失。

節點內部通訊機制

在分佈式的存儲方式中,還有另一個點須要咱們關注,那就是集羣之間的內部通訊方式,畢竟整個集羣是須要知道當前集羣中有多少有效的節點,這些節點分配的 ip 以及一些其餘的數據,這些數據咱們能夠稱之爲元數據。

集羣元數據的維護有兩種方式:集中式、 Gossip 協議。而 Redis Cluster 節點間採用 Gossip 協議進行通訊。

集中式是將集羣元數據(節點信息、故障等等)幾種存儲在某個節點上。集中式元數據集中存儲的一個典型表明,就是大數據領域的 storm 。它是分佈式的大數據實時計算引擎,是集中式的元數據存儲的結構,底層基於 zookeeper (分佈式協調的中間件)對全部元數據進行存儲維護。

Redis 維護集羣元數據採用另外一個方式, Gossip 協議,全部節點都持有一份元數據,不一樣的節點若是出現了元數據的變動,就不斷將元數據發送給其它的節點,讓其它節點也進行元數據的變動。

Gossip 協議

Gossip 協議是一種很是有意思的協議,它的過程是由一個種子節點發起,它會隨機的選擇周圍幾個節點散播消息,收到消息的節點也會重複該過程,直至最終網絡中全部的節點都收到了消息。

這個過程可能須要必定的時間,因爲不能保證某個時刻全部節點都收到消息,可是理論上最終全部節點都會收到消息,所以它是一個最終一致性協議。

Gossip 協議的一些特性:

  • 擴展性:網絡能夠容許節點的任意增長和減小,新增長的節點的狀態最終會與其餘節點一致。
  • 容錯:網絡中任何節點的宕機和重啓都不會影響 Gossip 消息的傳播,Gossip 協議具備自然的分佈式系統容錯特性。
  • 去中心化: Gossip 協議不要求任何中心節點,全部節點均可以是對等的,任何一個節點無需知道整個網絡情況,只要網絡是連通的,任意一個節點就能夠把消息散播到全網。
  • 一致性收斂: Gossip 協議中的消息會以一傳10、十傳百同樣的指數級速度在網絡中快速傳播,所以系統狀態的不一致能夠在很快的時間內收斂到一致。消息傳播速度達到了 logN。
  • 消息的延遲:因爲 Gossip 協議中,節點只會隨機向少數幾個節點發送消息,消息最終是經過多個輪次的散播而到達全網的,所以使用 Gossip 協議會形成不可避免的消息延遲。不適合用在對實時性要求較高的場景下。
  • 消息冗餘: Gossip 協議規定,節點會按期隨機選擇周圍節點發送消息,而收到消息的節點也會重複該步驟,所以就不可避免的存在消息重複發送給同一節點的狀況,形成了消息的冗餘,同時也增長了收到消息的節點的處理壓力。並且,因爲是按期發送,所以,即便收到了消息的節點還會反覆收到重複消息,加劇了消息的冗餘。

Gossip 協議包含多種消息,包含 ping , pong , meet , fail 等等。

  • meet:某個節點發送 meet 給新加入的節點,讓新節點加入集羣中,而後新節點就會開始與其它節點進行通訊。
  • ping:每一個節點都會頻繁給其它節點發送 ping,其中包含本身的狀態還有本身維護的集羣元數據,互相經過 ping 交換元數據。
  • pong:返回 ping 和 meeet,包含本身的狀態和其它信息,也用於信息廣播和更新。
  • fail:某個節點判斷另外一個節點 fail 以後,就發送 fail 給其它節點,通知其它節點說,某個節點宕機啦。

因爲 Redis Cluster 節點之間會按期交換 Gossip 消息,以及作一些心跳檢測,對網絡帶寬的壓力還有比較大的,包括官方都直接建議 Redis Cluster 節點數量不要超過 1000 個,當集羣中節點數量過多的時候,會產生不容忽視的帶寬消耗。

高可用和主備切換

一說到集羣就不得不提升可用和主備切換,而Redis cluster 的高可用的原理,幾乎跟哨兵是相似的。

判斷宕機

首先第一步固然是判斷宕機,這和哨兵模式很是的像,一樣是分紅兩種類型,一種是主觀宕機 pfail ,另外一種的客觀宕機 fail

  • 主觀宕機:一個節點認爲另一個節點宕機,這是一種「偏見」,並不表明整個集羣的認知。
  1. 節點 1 按期發送 ping 消息給節點 2 。
  2. 若是發送成功,表明節點 2 正常運行,節點 2 會響應 PONG 消息給節點 1 ,節點 1 更新與節點 2 的最後通訊時間
  3. 若是發送失敗,則節點 1 與節點 2 之間的通訊異常判斷鏈接,在下一個定時任務週期時,仍然會與節點 2 發送 ping 消息
  4. 若是節點 1 發現與節點 2 最後通訊時間超過 cluster-node-timeout ,則把節點 2 標識爲 pfail 狀態。
  • 客觀宕機:當半數以上持有槽的主節點都標記某節點主觀下線。
  1. 當節點 1 認爲節點 2 宕機後,那麼會在 Gossip ping 消息中, ping 給其餘節點。
  2. 當其餘節點會從新嘗試以前節點 1 主觀宕機的過程。
  3. 若是有超過半數的節點都認爲 pfail 了,那麼這個節點 2 就會變成真的 fail

注意:集羣模式下,只有主節點( master )纔有讀寫權限和集羣槽的維護權限,從節點( slave )只有複製的權限。

從節點選舉

  1. 先檢查資格,檢查全部的 salve 與 master 斷開鏈接的時間,若是超過了 cluster-node-timeout * cluster-slave-validity-factor ,那麼久沒有資格成爲 master 。
  2. 每一個從節點,都根據本身對 master 複製數據的 offset,來設置一個選舉時間,offset 越大(複製數據越多)的從節點,選舉時間越靠前,優先進行選舉。
  3. 全部的 master 開始選舉投票,若是大部分 master 節點 (N/2 + 1) 都投票給了某個從節點,那麼選舉經過,那個從節點能夠切換成 master 。
  4. 從節點執行主備切換,從節點切換爲主節點。

參考

www.cnblogs.com/williamjie/…

github.com/doocs/advan…

zhuanlan.zhihu.com/p/41228196

您的掃碼關注,是對小編堅持原創的最大鼓勵:)
相關文章
相關標籤/搜索