建議前面文章沒看過的同窗先看下前面的文章: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 算法是對 key 進行 hash 運算後取值,而後對節點的數量取模。
接着將 key 存入對應的節點,可是,一旦其中某個節點宕機,全部的請求過來,都會基於最新的存活的節點數量進行取模運算,這就會致使大多數的請求沒法拿到緩存數據。
舉個例子,一開始我有 3 個節點,這時你們正常的取模運算將數據基本均勻的存在了 3 個節點上,忽然宕機了一臺,如今只剩下 2 個有效的節點了,這時全部的運算都會基於最新的 2 個節點進行取模運算,原來的 key 對 2 進行取模運算後,顯然和對 3 進行取模運算獲得的結果是不同的。
結果是大量的數據請求會直接打在 DB 上,這是不可容忍的。
一致性 hash 算法將整個 hash 值空間組織成一個虛擬的圓環,整個空間按順時針方向組織,下一步將各個 master 節點(使用服務器的 ip 或主機名)進行 hash。這樣就能肯定每一個節點在其哈希環上的位置。
來看一個經典的一致性 hash 算法的環狀示意圖:
來了一個 key,首先計算 hash 值,並肯定此數據在環上的位置,今後位置沿環順時針「行走」,遇到的第一個 master 節點就是 key 所在位置。
在一致性哈希算法中,若是一個節點掛了,受影響的數據僅僅是此節點到環空間前一個節點(沿着逆時針方向行走遇到的第一個節點)之間的數據,其它不受影響。增長一個節點也同理。
一致性哈希算法在節點太少時,容易由於節點分佈不均勻而形成緩存熱點的問題。爲了解決這種熱點問題,一致性 hash 算法引入了虛擬節點機制,即對每個節點計算多個 hash,每一個計算結果位置都放置一個虛擬節點。這樣就實現了數據的均勻分佈,負載均衡。
Redis Cluster 的實現方案十分的聰明,它的分區方式採用了虛擬槽分區。
Redis Cluster 首先會預設虛擬槽,每一個槽就至關於一個數字,有必定範圍,每一個槽映射一個數據子集。
Redis Cluster中預設虛擬槽的範圍爲 0 到 16383
注意:Redis Cluster 的節點之間會共享消息,每一個節點都會知道是哪一個節點負責哪一個範圍內的數據槽
虛擬槽分佈方式中,因爲每一個節點管理一部分數據槽,數據保存到數據槽中。當節點擴容或者縮容時,對數據槽進行從新分配遷移便可,數據不會丟失。
在分佈式的存儲方式中,還有另一個點須要咱們關注,那就是集羣之間的內部通訊方式,畢竟整個集羣是須要知道當前集羣中有多少有效的節點,這些節點分配的 ip 以及一些其餘的數據,這些數據咱們能夠稱之爲元數據。
集羣元數據的維護有兩種方式:集中式、 Gossip 協議。而 Redis Cluster 節點間採用 Gossip 協議進行通訊。
集中式是將集羣元數據(節點信息、故障等等)幾種存儲在某個節點上。集中式元數據集中存儲的一個典型表明,就是大數據領域的 storm
。它是分佈式的大數據實時計算引擎,是集中式的元數據存儲的結構,底層基於 zookeeper
(分佈式協調的中間件)對全部元數據進行存儲維護。
Redis 維護集羣元數據採用另外一個方式, Gossip
協議,全部節點都持有一份元數據,不一樣的節點若是出現了元數據的變動,就不斷將元數據發送給其它的節點,讓其它節點也進行元數據的變動。
Gossip 協議是一種很是有意思的協議,它的過程是由一個種子節點發起,它會隨機的選擇周圍幾個節點散播消息,收到消息的節點也會重複該過程,直至最終網絡中全部的節點都收到了消息。
這個過程可能須要必定的時間,因爲不能保證某個時刻全部節點都收到消息,可是理論上最終全部節點都會收到消息,所以它是一個最終一致性協議。
Gossip 協議的一些特性:
Gossip 協議包含多種消息,包含 ping , pong , meet , fail 等等。
因爲 Redis Cluster 節點之間會按期交換 Gossip 消息,以及作一些心跳檢測,對網絡帶寬的壓力還有比較大的,包括官方都直接建議 Redis Cluster 節點數量不要超過 1000 個,當集羣中節點數量過多的時候,會產生不容忽視的帶寬消耗。
一說到集羣就不得不提升可用和主備切換,而Redis cluster 的高可用的原理,幾乎跟哨兵是相似的。
首先第一步固然是判斷宕機,這和哨兵模式很是的像,一樣是分紅兩種類型,一種是主觀宕機 pfail
,另外一種的客觀宕機 fail
。
cluster-node-timeout
,則把節點 2 標識爲 pfail 狀態。pfail
了,那麼這個節點 2 就會變成真的 fail
。注意:集羣模式下,只有主節點( master )纔有讀寫權限和集羣槽的維護權限,從節點( slave )只有複製的權限。
cluster-node-timeout * cluster-slave-validity-factor
,那麼久沒有資格成爲 master 。