不想談好吉他的擼鐵狗,不是好的程序員,歡迎微信關注「SH的全棧筆記」node
上文咱們聊了基於Sentinel的Redis高可用架構,瞭解了Redis基於讀寫分離的主從架構,同時也知道當Redis的master發生故障以後,Sentinel集羣是如何執行failover的,以及其執行failover的原理是什麼。程序員
這裏大概再提一下,Sentinel集羣會對Redis的主從架構中的Redis實例進行監控,一旦發現了master節點宕機了,就會選舉出一個Sentinel節點來執行故障轉移,從原來的slave節點中選舉出一個,將其提高爲master節點,而後讓其餘的節點去複製新選舉出來的master節點。web
你可能會以爲這樣沒有問題啊,甚至可以知足咱們生產環境的使用需求了,那咱們爲何還須要Redis Cluster呢?redis
的確,在數據上,有replication副本作保證;可用性上,master宕機會自動的執行failover。算法
那問題在哪兒呢?數據庫
首先Redis Sentinel說白了也是基於主從複製,在主從複製中slave的數據是徹底來自於master。緩存
假設master節點的內存只有4G,那slave節點所能存儲的數據上限也只能是4G。並且在以前的跟隨槓精的視角一塊兒來了解Redis的主從複製文章中也說過,主從複製架構中是讀寫分離的,咱們能夠經過增長slave節點來擴展主從的讀併發能力,可是寫能力和存儲能力是沒法進行擴展的,就只能是master節點可以承載的上限。微信
因此,當你只須要存儲4G的數據時候的,基於主從複製和基於Sentinel的高可用架構是徹底夠用的。網絡
可是若是當你面臨的是海量的數據的時候呢?16G、64G、256G甚至1T呢?如今互聯網的業務裏面,若是你的體量足夠大,我以爲是確定會面臨緩存海量緩存數據的場景的。數據結構
這就是爲何咱們須要引入Redis Cluster。
知道了爲何須要Redis Cluster以後,咱們就能夠來對其一探究竟了。
那什麼是Redis Cluster呢?
很簡單,你就能夠理解爲n個主從架構組合在一塊兒對外服務。Redis Cluster要求至少須要3個master才能組成一個集羣,同時每一個master至少須要有一個slave節點。
這樣一來,若是一個主從可以存儲32G的數據,若是這個集羣包含了兩個主從,則整個集羣就可以存儲64G的數據。
咱們知道,主從架構中,能夠經過增長slave節點的方式來擴展讀請求的併發量,那Redis Cluster中是如何作的呢?雖然每一個master下都掛載了一個slave節點,可是在Redis Cluster中的讀、寫請求其實都是在master上完成的。
slave節點只是充當了一個數據備份的角色,當master發生了宕機,就會將對應的slave節點提拔爲master,來從新對外提供服務。
知道了什麼是Redis Cluster,咱們就能夠繼續下面的討論了。
不知道你思考過一個問題沒,這麼多的master節點。我存儲的時候,到底該選擇哪一個節點呢?通常這種負載均衡算法,會選擇哈希算法。哈希算法是怎麼作的呢?
首先就是對key計算出一個hash值,而後用哈希值對master數量進行取模。由此就能夠將key負載均衡到每個Redis節點上去。這就是簡單的哈希算法的實現。
那Redis Cluster是採起的上面的哈希算法嗎?答案是沒有。
Redis Cluster其實採起的是相似於一致性哈希的算法來實現節點選擇的。那爲何不用哈希算法來進行實例選擇呢?以及爲何說是相似的呢?咱們繼續討論。
由於若是此時某一臺master發生了宕機,那麼此時會致使Redis中全部的緩存失效。爲何是全部的?假設以前有3個master,那麼以前的算法應該是 hash % 3,可是若是其中一臺master宕機了,則算法就會變成 hash % 2,會影響到以前存儲的全部的key。而這對緩存後面保護的DB來講,是致命的打擊。
知道了經過傳統哈希算法來實現對節點的負載均衡的弊端,咱們就須要進一步瞭解什麼是一致性哈希。
咱們上面提過哈希算法是對master實例數量來取模,而一致性哈希則是對2^32取模,也就是值的範圍在[0, 2^32 -1]。一致性哈希將其範圍抽象成了一個圓環,使用CRC16算法計算出來的哈希值會落到圓環上的某個地方。
而後咱們的Redis實例也分佈在圓環上,咱們在圓環上按照順時針的順序找到第一個Redis實例,這樣就完成了對key的節點分配。咱們舉個例子。
假設咱們有A、B、C三個Redis實例按照如圖所示的位置分佈在圓環上,此時計算出來的hash值,取模以後位置落在了位置D,那麼咱們按照順時針的順序,就可以找到咱們這個key應該分配的Redis實例B。同理若是咱們計算出來位置在E,那麼對應選擇的Redis的實例就是A。
即便這個時候Redis實例B掛了,也不會影響到實例A和C的緩存。
例如此時節點B掛了,那以前計算出來在位置D的key,此時會按照順時針的順序,找到節點C。至關於自動的把原來節點B的流量給轉移到了節點C上去。而其餘本來就在節點A和節點C的數據則徹底不受影響。
這就是一致性哈希,可以在咱們後續須要新增節點或者刪除節點的時候,不影響其餘節點的正常運行。
可是一致性哈希也存在自身的小問題,例如當咱們的Redis節點分佈以下時,就有問題了。
此時數據落在節點A上的機率明顯是大於其餘兩個節點的,其次落在節點C上的機率最小。這樣一來會致使整個集羣的數據存儲不平衡,AB節點壓力較大,而C節點資源利用不充分。爲了解決這個問題,一致性哈希算法引入了虛擬節點機制。
在圓環中,增長了對應節點的虛擬節點,而後完成了虛擬節點到真實節點的映射。假設如今計算得出了位置D,那麼按照順時針的順序,咱們找到的第一個節點就是C #1,最終數據實際仍是會落在節點C上。
經過增長虛擬節點的方式,使ABC三個節點在圓環上的位置更加均勻,平均了落在每個節點上的機率。這樣一來就解決了上文提到的數據存儲存在不均勻的問題了,這就是一致性哈希的虛擬節點機制。
上面提到過,Redis Cluster採用的是類一致性哈希算法,之因此是類一致性哈希算法是由於它們實現的方式還略微有差異。
例如一致性哈希是對2^32取模,而Redis Cluster則是對2^14(也就是16384)取模。Redis Cluster將本身分紅了16384個Slot(槽位)。經過CRC16算法計算出來的哈希值會跟16384取模,取模以後獲得的值就是對應的槽位,而後每一個Redis節點都會負責處理一部分的槽位,就像下表這樣。
節點 | 處理槽位 |
---|---|
A | 0 - 5000 |
B | 5001 - 10000 |
C | 10001 - 16383 |
每一個Redis實例會本身維護一份slot - Redis節點的映射關係,假設你在節點A上設置了某個key,可是這個key經過CRC16計算出來的槽位是由節點B維護的,那麼就會提示你須要去節點B上進行操做。
不知道你思考過一個問題沒,若是Redis Cluster中的某個master節點掛了,它是如何保證集羣自身的高可用的?若是這個時候咱們集羣須要擴容節點,它該負責哪些槽位呢?咱們一個一個問題的來看一下。
咱們開篇聊過,Redis Cluster能夠很方便的進行橫向擴容,那當新的節點加入進來的時候,它是如何獲取對應的slot的呢?
答案是經過reshard(從新分片)來實現。reshard能夠將已經分配給某個節點的任意數量的slot遷移給另外一個節點,在Redis內部是由redis-trib負責執行的。你能夠理解爲Redis其實已經封裝好了全部的命令,而redis-trib則負責向獲取slot的節點和被轉移slot的節點發送命令來最終實現reshard。
假設咱們須要向集羣中加入一個D節點,而此時集羣內已經有A、B、C三個節點了。
此時redis-trib會向A、B、C三個節點發送遷移出槽位的請求,同時向D節點發送準備導入槽位的請求,作好準備以後A、B、C這三個源節點就開始執行遷移,將對應的slot所對應的鍵值對遷移至目標節點D。最後redis-trib會向集羣中全部主節點發送槽位的變動信息。
Redis Cluster中保證集羣高可用的思路和實現和Redis Sentinel一模一樣,感興趣的能夠去看我以前寫的關於Sentinel的文章Redis Sentinel-深刻淺出原理和實戰。
簡單來講,針對A節點,某一個節點認爲A宕機了,那麼此時是主觀宕機。而若是集羣內超過半數的節點認爲A掛了, 那麼此時A就會被標記爲客觀宕機。
一旦節點A被標記爲了客觀宕機,集羣就會開始執行故障轉移。其他正常運行的master節點會進行投票選舉,從A節點的slave節點中選舉出一個,將其切換成新的master對外提供服務。當某個slave得到了超過半數的master節點投票,就成功當選。
當選成功以後,新的master會執行slaveof no one
來讓本身中止複製A節點,使本身成爲master。而後將A節點所負責處理的slot,所有轉移給本身,而後就會向集羣發PONG消息來廣播本身的最新狀態。
按照一致性哈希的思想,若是某個節點掛了,那麼就會沿着那個圓環,按照順時針的順序找到遇到的第一個Redis實例。
而對於Redis Cluster,某個key它其實並不關心它最終要去到哪一個節點,他只關心他最終落到哪一個slot上,不管你節點怎麼去遷移,最終仍是隻須要找到對應的slot,而後再找到slot關聯的節點,最終就可以找到最終的Redis實例了。
那這個PONG消息又是什麼東西呢?別急,下面就會聊到。
這就是Redis Cluster各個節點之間交換數據、通訊所採用的一種協議,叫作gossip。
gossip: 流言、八卦、小道消息
gossip是在1989年的論文上提出的,我看了一堆資料都說的是1987年發表的,可是文章裏的時間明確是1989年1月份發表。
感興趣的能夠去看看Epidemic Algorithms for Replicated . Database Maintenance,在當時提出gossip主要是爲了解決在分佈式數據庫中,各個副本節點的數據同步問題。但隨着技術的發展,gossip後續也被普遍運用於信息擴散、故障探測等等。
Redis Cluster就是利用了gossip來實現自身的信息擴散的。那使用gossip具體是如何通訊的呢?
很簡單,就像圖裏這樣。每一個Redis節點每秒鐘都會向其餘的節點發送PING,而後被PING的節點會回一個PONG。
Redis Cluster中,節點之間的消息類型有5種,分別是MEET、PING、PONG、FAIL和PUBLISH。這些消息分別傳遞了什麼內容呢?我簡單總結了一下。
消息類型 | 消息內容 |
---|---|
MEET | 給某個節點發送MEET消息,請求接收消息的節點加入到集羣中 |
PING | 每隔一秒鐘,選擇5個最久沒有通訊的節點,發送PING消息,檢測對應的節點是否在線;同時還有一種策略是,若是某個節點的通訊延遲大於了cluster-node-time 的值的一半,就會當即給該節點發送PING消息,避免數據交換延遲太久 |
PONG | 當節點接收到MEET或者PING消息以後,會回一個PONG消息給發送方,表明本身收到了MEET或者PING消息。同時,節點也能夠主動的經過PONG消息向集羣中廣播本身的信息,讓其餘節點獲取到本身最新的屬性,就像完成了故障轉移以後新的master向集羣發送PONG消息同樣 |
FAIL | 用於廣播本身的對某個節點的宕機判斷,假設當前節點對A節點判斷爲宕機,就會當即向Redis Cluster廣播本身對於A節點的判斷,全部收到消息的節點就會對A節點作標記 |
PUBLISH | 用於向指定的Channel發送消息,某個節點收到PUBLISH消息以後會直接在集羣內廣播,這樣一來,客戶端不管鏈接到任何節點都可以訂閱這個Channel |
既然Redis Cluster選擇了gossip,那確定存在一些gossip的優勢,咱們接下來簡單梳理一下。
優勢 | 描述 |
---|---|
擴展性 | 網絡能夠容許節點的任意增長和減小,新增長的節點的狀態最終會與其餘節點一致。 |
容錯性 | 因爲每一個節點都持有一份完整元數據,因此任何節點宕機都不會影響gossip的運行 |
健壯性 | 與容錯性相似,因爲全部節點都持有數據,地位平臺,是一個去中心化的設計,任何節點都不會影響到服務的運行 |
最終一致性 | 當有新的信息須要傳遞時,消息能夠快速的發送到全部的節點,讓全部的節點都擁有最新的數據 |
gossip能夠在O(logN) 輪就能夠將信息傳播到全部的節點,爲何是O(logN)呢?由於每次ping,當前節點會帶上本身的信息外加整個Cluster的1/10數量的節點信息,一塊兒發送出去。你能夠簡單的把這個模型抽象爲:
你轉發了一個特別有意思的文章到朋友圈,而後你的朋友們都以爲還不錯,因而就一傳10、十傳百這樣的散播出去了,這就是朋友圈的裂變傳播。
固然,gossip仍然存在一些缺點。例如消息可能最終會通過不少輪才能到達目標節點,而這可能會帶來較大的延遲。同時因爲節點會隨機選出5個最久沒有通訊的節點,這可能會形成某一個節點同時收到n個重複的消息。
總的來講,Redis Cluster至關因而把Redis的主從架構和Sentinel集成到了一塊兒,從Redis Cluster的高可用機制、判斷故障轉移以及執行故障轉移的過程,都和主從、Sentinel相關,這也是爲何我在以前的文章裏說,主從是Redis高可用架構的基石。
好了以上就是本篇博客的所有內容了,若是你以爲這篇文章對你有幫助,還麻煩點個贊,關個注,分個享,留個言。
歡迎微信搜索關注【SH的全棧筆記】,查看更多相關文章
- END -推薦閱讀: