Redis sentinel雖然解決了Redis的自動故障轉移,可是真正使用的仍是單機。當併發量很高的時候,會遇到內存、併發、存儲等的瓶頸,這就須要使用Redis的分佈式架構來達到負載均衡。node
Redis Sharding能夠說是Redis Cluster出來以前,業界廣泛使用的多Redis實例集羣方法。其主要思想是採用哈希算法將Redis數據的key進行散列,經過hash函數,特定的key會映射到特定的Redis節點上。這樣,客戶端就知道該向哪一個Redis節點操做數據。這是一種客戶端分區的實現。客戶端分片是把分片的邏輯放在Redis客戶端實現,經過Redis客戶端預先定義好的路由規則,把對Key的訪問轉發到對應的Redis實例上進行。 redis
Codis是一個分佈式的Redis解決方案(前豌豆莢團隊開源),對於上層的應用來講,鏈接Codis Proxy和鏈接原生的Redis Server沒有明顯的區別(不支持的命令列表),上層應用能夠像使用單機的Redis同樣使用,Codis底層會處理請求的轉發,不停機的數據遷移等工做,全部後邊的一切事情,對於前面客戶端來講是透明的,能夠簡單的認爲後邊鏈接是一個內存無限大的Redis服務。這是一種基於中間件的代理方案,要實現Codis的高可用須要部署多個Codis實例。
算法
Codis 將全部的 key 默認劃分爲 1024 個槽位(slot),它首先對客戶端傳過來的 key 進行 crc32 運算計算哈希值,再將 hash 後的整數值對 1024 這個整數進行取模獲得一個餘數,這個餘數就是對應 key 的槽位。每一個槽位都會惟一映射到後面的多個 Redis 實例之一,Codis 會在內存維護槽位和 Redis 實例的映射關係。多個Codis實例之間槽位關係須要經過Zookeeper來維護,因此這又增長了運維的負擔。數據庫
Redis 3正式推出了官方集羣技術,解決了多Redis實例協同服務問題。Redis Cluster能夠說是服務端Sharding分片技術的體現,即將鍵值按照必定算法合理分配到各個實例分片上,同時各個實例節點協調溝通,共同對外承擔一致服務。但實現原理與客戶端sharding分片有所不一樣,redis cluster引入了槽(相似Codis,但cluster劃分出了16384個槽),將全部的數據對應到槽中,而後每一個Redis節點管理一部分槽。緩存
要將整個數據集劃分到多個節點上,讓每一個節點負責一部分的數據,就須要使用數據分區規則。經常使用的分區規則是哈希分區和順序分區。網絡
Redis Cluster採用的是哈希分區規則,經常使用的哈希分區規則也有幾種:固定哈希分區、一致性哈希分區、虛擬槽哈希分區。架構
根據特定的字段(Redis中就是使用鍵)使用哈希函數計算出Hash值,而後根據節點的數量N取模,來決定將數據映射到哪個節點中。這種方式優勢就是規則設置和實現都很簡單,缺點就是擴容或收縮節點會涉及到不少數據的遷移。
併發
一致性哈希能夠很好的解決穩定性問題,能夠將全部的存儲節點排列在首尾相接的Hash環上(環的大小爲2 ^ 32),key在計算Hash後會 按順時針方向找到最近的存儲節點存放數據。而當新增或減小節點時,僅影響該節點在Hash環上順時針方向的下一個節點。負載均衡
當集羣須要擴容時,只會影響環上順時針方向的下一個節點(會給該節點減小負載)。以下圖:
運維
新增的node5爲node4分擔了一部分負載,node1到node5之間的key中的數據將會往node5中遷移(若是是用做緩存的話就直接失效),可是對其它節點沒有任何影響。
當集羣須要縮減時,減小一個節點,也只會影響順時針方向的下一個節點(這個節點將承接移除節點的全部負載)。
移除node1,那麼node1以前的全部數據將會往node5遷移(若是是緩存會直接失效),node5將要承接node1以前的全部負載。可是對於集羣中的其餘節點仍是沒有任何影響。
經過上面能夠發現,一致性哈希分區規則在集羣擴容或縮減時並不能達到負載均衡的目的(除非每次增長一倍或減去一半),因此通常分佈式系統都會採用虛擬節點(虛擬槽)對一致性哈希進行改進。
虛擬槽分區巧妙地使用了哈希空間,使用分散度良好的哈希算法(Redis使用的是CRC16算法)把全部數據映射到一個固定範圍的整數集合中,整數定義爲槽(slot)。這個範圍通常遠遠大於節點數,好比 Redis Cluster 槽範圍是 0 ~ 16383。 槽是集羣內數據管理和遷移的基本單位,每一個節點都負責必定量的槽,先經過hash函數將鍵映射到槽中,而後將數據存儲在該槽所對應的節點。這樣就解耦了節點和數據的鍵之間的關係(鍵不是直接映射到節點上,而是映射到槽上),簡化了節點的擴容和收縮。
經過虛擬槽哈希分區,能夠輕鬆的實現節點的添加和刪除。若是我想添加一個新節點Node4,則只須要將一些哈希槽從節點Node1,Node2,Node3移到新節點Node4。相似地,若是我想從集羣中刪除節點Node1,則只需將Node1所負責的哈希槽移動到Node2和Node3便可。當節點Node1上的槽所有移走時,就能夠將其從羣集中徹底刪除。
由於將哈希槽從一個節點移動到另外一個節點不須要中止操做,因此添加和刪除節點或更改節點持有的哈希槽不須要任何停機時間,這保證了集羣的高可用。
在分佈式存儲中須要提供維護節點元數據的機制,元數據就是指節點負責哪些數據(在Redis中就是指負責哪些槽)以及節點的狀態。經常使用的元數據維護方式爲集中式和P2P式。Redis Cluster採用的是P2P的Gossip協議。
這個協議的做用就像其名字表示的意思同樣,很是容易理解,它的方式其實在咱們平常生活中也很常見,好比電腦病毒的傳播,森林大火,細胞擴散,傳染病傳播等等。
集羣中的每一個節點會單獨開通一個TCP通道,用於節點之間彼此的通訊(端口號爲節點端口加上10000)。
當有節點更新了狀態(新節點加入、節點故障、主從角色變化、槽信息變化等)時,該節點會隨機向周圍幾個節點傳播消息,收到消息的節點會重複這個過程,最終集羣中全部的節點都會收到該消息,達到集羣狀態最終一致的目的。
可擴展性:網絡能夠容許節點的任意增長和減小,新增長的節點的狀態最終會與其餘節點一致。
容錯:網絡中任何節點的宕機和重啓都不會影響Gossip消息的傳播,Gossip 協議具備自然的分佈式系統容錯特性。
去中心化:Gossip協議不要求任何中心節點,全部節點均可以是對等的,任何一個節點無需知道整個網絡情況,只要網絡是連通的,任意一個節點就能夠把消息散播到全網。
最終一致性:Gossip協議實現信息指數級(logN)的快速傳播,所以在有新信息須要傳播時,消息能夠快速地發送到全局節點,在有限的時間內可以作到全部節點都擁有最新的數據。
消息的延遲:因爲Gossip協議中,節點只會隨機向少數幾個節點發送消息,消息最終是經過多個輪次的散播而到達全網的,所以使用 Gossip 協議會形成不可避免的消息延遲。不適合用在對實時性要求較高的場景下。
消息冗餘:Gossip協議規定,節點會按期隨機選擇周圍節點發送消息,而收到消息的節點也會重複該步驟,所以就不可避免的存在消息重複發送給同一節點的狀況,形成了消息的冗餘,同時也增長了收到消息的節點的處理壓力。並且,因爲是按期發送,所以,即便收到了消息的節點還會反覆收到重複消息,加劇了消息的冗餘。