Redis集群分片原理及選舉流程
集群分片模式
如果Redis只用復制功能做主從,那么當數據量巨大的情況下,單機情況下可能已經承受不下一份數據,更不用說是主從都要各自保存一份完整的數據。在這種情況下,數據分片是一個非常好的解決辦法。
Redis的Cluster正是用於解決該問題。它主要提供兩個功能:
- 自動對數據分片,落到各個節點上
- 即使集群部分節點失效或者連接不上,依然可以繼續處理命令
對於第二點,它的功能有點類似於Sentienl的故障轉移,在這里不細說。下面詳細了解下Redis的槽位分片原理,在此之前,先了解下分布式簡單哈希算法和一致性哈希算法,以幫助理解槽位的作用。
簡單哈希算法
假設有三台機,數據落在哪台機的算法為
c = Hash(key) % 3
例如key A的哈希值為4,4%3=1,則落在第二台機。Key ABC哈希值為11,11%3=2,則落在第三台機上。
利用這樣的算法,假設現在數據量太大了,需要增加一台機器。A原本落在第二台上,現在根據算法4%4=0,落到了第一台機器上了,但是第一台機器上根本沒有A的值。這樣的算法會導致增加機器或減少機器的時候,引起大量的緩存擊穿,造成雪崩。
緩存擊穿:查詢的數據在數據庫中是存在,但是緩存中熱點數據key恰巧失效,所有請求直接懟到數據庫導致數據庫連接等待甚至宕機
【加鎖,雙緩存[A,B]{A 緩存的key需要設置過期時間,而B緩存的key永不失效}】JUC (讀寫鎖)
保證一致性: 在更新數據到數據庫之后刪除緩存在重新寫入緩存。
緩存雪崩:查詢的時候大面積的key集體失效導致請求直接到了數據庫。(隨機范圍設置過期時間)
// select* from table where id = -1111 (壞人)
緩存穿透:查詢的數據在數據庫中不存在,也就意味沒有命中緩存中key,導致請求全部到數據操作。(布隆過濾器,緩存空數據)
一致性哈希算法
在1997年,麻省理工學院的Karger等人提出了一致性哈希算法,為的就是解決分布式緩存的問題。
在一致性哈希算法中,整個哈希空間是一個虛擬圓環
假設有四個節點Node A、B、C、D,經過ip地址的哈希計算,它們的位置如下
有4個存儲對象Object A、B、C、D,經過對Key的哈希計算后,它們的位置如下
對於各個Object,它所真正的存儲位置是按順時針找到的第一個存儲節點。例如Object A順時針找到的第一個節點是Node A,所以Node A負責存儲Object A,Object B存儲在Node B。
一致性哈希算法大概如此,那么它的容錯性和擴展性如何呢?
假設Node C節點掛掉了,Object C的存儲丟失,那么它順時針找到的最新節點是Node D。也就是說Node C掛掉了,受影響僅僅包括Node B到Node C區間的數據,並且這些數據會轉移到Node D進行存儲。
同理,假設現在數據量大了,需要增加一台節點Node X。Node X的位置在Node B到Node C直接,那么受到影響的僅僅是Node B到Node X間的數據,它們要重新落到Node X上。
所以一致性哈希算法對於容錯性和擴展性有非常好的支持。但一致性哈希算法也有一個嚴重的問題,就是數據傾斜。
如果在分片的集群中,節點太少,並且分布不均,一致性哈希算法就會出現部分節點數據太多,部分節點數據太少。也就是說無法控制節點存儲數據的分配。如下圖,大部分數據都在A上了,B的數據比較少。
哈希槽
Redis集群(Cluster)並沒有選用上面一致性哈希,而是采用了哈希槽(SLOT)的這種概念。主要的原因就是上面所說的,一致性哈希算法對於數據分布、節點位置的控制並不是很友好。
首先哈希槽其實是兩個概念,第一個是哈希算法。Redis Cluster的hash算法不是簡單的hash(),而是crc16算法,一種校驗算法。
另外一個就是槽位的概念,空間分配的規則。其實哈希槽的本質和一致性哈希算法非常相似,不同點就是對於哈希空間的定義。一致性哈希的空間是一個圓環,節點分布是基於圓環的,無法很好的控制數據分布。而Redis Cluster的槽位空間是自定義分配的,類似於Windows盤分區的概念。這種分區是可以自定義大小,自定義位置的。
Redis Cluster包含了16384個哈希槽,每個Key通過計算后都會落在具體一個槽位上,而這個槽位是屬於哪個存儲節點的,則由用戶自己定義分配。例如機器硬盤小的,可以分配少一點槽位,硬盤大的可以分配多一點。如果節點硬盤都差不多則可以平均分配。所以哈希槽這種概念很好地解決了一致性哈希的弊端。
另外在容錯性和擴展性上,表象與一致性哈希一樣,都是對受影響的數據進行轉移。而哈希槽本質上是對槽位的轉移,把故障節點負責的槽位轉移到其他正常的節點上。擴展節點也是一樣,把其他節點上的槽位轉移到新的節點上。
但一定要注意的是,對於槽位的轉移和分派,Redis集群是不會自動進行的,而是需要人工配置的。所以Redis集群的高可用是依賴於節點的主從復制與主從間的自動故障轉移。
集群選舉原理分析
選舉流程:
原理分析:
當slave發現自己的master變為FAIL狀態時,便嘗試進行Failover,以期成為新的master。由於掛掉的master可能會有多個slave,從而存在多個slave競爭成為master節點的過程, 其過程如下:
1.slave發現自己的master變為FAIL
2.將自己記錄的集群currentEpoch加1,並廣播FAILOVER_AUTH_REQUEST信息
3.其他節點收到該信息,只有master響應,判斷請求者的合法性,並發送FAILOVER_AUTH_ACK,對每一個epoch只發送一次ack
4.嘗試failover的slave收集FAILOVER_AUTH_ACK
5.超過半數后變成新Master
6.廣播Pong通知其他集群節點。從節點並不是在主節點一進入 FAIL 狀態就馬上嘗試發起選舉,而是有一定延遲,一定的延遲確保我們等待FAIL狀態在集群中傳播,slave如果立即嘗試選舉,其它masters或許尚未意識到FAIL狀態,可能會拒絕投票
延遲計算公式:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
1
SLAVE_RANK表示此slave已經從master復制數據的總量的rank。Rank越小代表已復制的數據越新。這種方式下,持有最新數據的slave將會首先發起選舉(理論上)。補充之前的一個問題:
跳轉重定位
當客戶端向一個錯誤的節點發出了指令,該節點會發現指令的 key 所在的槽位並不歸自己管理,這時它會向客戶端發送一個特殊的跳轉指令攜帶目標操作的節點地址,告訴客戶端去連這個節點去獲取數據。客戶端收到指令后除了跳轉到正確的節點上去操作,還會同步更新糾正本地的槽位映射表緩存,后續所有 key 將使用新的槽位映射表。