天天看點

由Redis Cluster叢集引發的對幾種算法的思考

作者:Java網際網路技術棧

對比幾個相似算法,了解Redis Cluster叢集所使用算法的原因。首先介紹一下單調性:

單調性是指如果已經有一些内容通過哈希分派到了相應的緩沖中,又有新的緩沖加入到系統中。哈希的結果應能夠保證原有已配置設定的内容可以被映射到新的緩沖中去,而不會被映射到舊的緩沖集合中的其他緩沖區。

一、HASH取餘算法

簡單公式:

hash(object)%N
           

應用場景:

比如你有 N 個 cache 伺服器(後面簡稱 cache ),那麼如何将一個對象 object 映射到 N 個 cache 上呢,你很可能會采用類似下面的通用方法計算 object 的 hash 值,然後均勻的映射到到 N 個 cache ;

一切都運作正常,再考慮如下的兩種情況;

1 一個 cache 伺服器 m down 掉了(在實際應用中必須要考慮這種情況),這樣所有映射到 cache m 的對象都會失效,怎麼辦,需要把 cache m 從 cache 中移除,這時候 cache 是 N-1 台,映射公式變成了 hash(object)%(N-1) ;

2 由于通路加重,需要添加 cache ,這時候 cache 是 N+1 台,映射公式變成了 hash(object)%(N+1) ;

1 和 2 意味着什麼?這意味着突然之間幾乎所有的 cache 都失效了。對于伺服器而言,這是一場災難,洪水般的通路都會直接沖向背景伺服器;

再來考慮第三個問題,由于硬體能力越來越強,你可能想讓後面添加的節點多做點活,顯然上面的 hash 算法也做不到。

hash取餘不滿足單調性原則。

有什麼方法可以改變這個狀況呢,這就是 一緻性hash。

二、一緻性hash算法

consistent hashing 是一種 hash 算法,簡單的說,在移除 / 添加一個 cache 時,它能夠盡可能小的改變已存在key 映射關系,盡可能的滿足單調性的要求。

在一緻性hash算法中,将0到2^32-1區間的數字按順時針形成一個圓環,如下圖所示(圖沒有截全,請自行腦補):

由Redis Cluster叢集引發的對幾種算法的思考

在redis叢集中,将叢集伺服器的ip或者伺服器名稱進行hash函數,然後對2^32取模,得到的數字在上述圓環中定位,得到伺服器在圓環中的位置。

當在redis叢集中存入key時,對key進行hash函數,然後進行2^32取模,得到的數值就是該key在hash環上的位置。然後從該位置起,順時針沿着圓環走,走到第一個伺服器的位置,就是該key存放的位置。如下圖所示:

由Redis Cluster叢集引發的對幾種算法的思考

ObjectA通過hash()函數計算并進行2^32取模後,得到在hash環上的位置,然後順時針找到第一個伺服器位置,就是ObjectA存放的位置。ObjectB也是同樣的道理。

這麼設計有何好處呢?我們看下圖:

由Redis Cluster叢集引發的對幾種算法的思考

在上圖中,假設C伺服器當機了,那麼此時,C伺服器中存放的key,會瞬移到D伺服器中。同時,新加入的資料,通過計算得到在hash環上的位置後,順時針查找伺服器也會直接跳過C,存放到D中。如此一來,伺服器當機不會影響到全部伺服器中資料存放。而是隻影響了D伺服器中資料的存放内容。這就避免了在hash取餘中當機一台伺服器,分母就會變化而導緻所有伺服器中資料都要變化的情況出現。

同樣的,當加入一台伺服器時,也是在hash環中查找加入的位置,新的資料順時針找到新加伺服器後,會存入新加的伺服器上,而不影響其他伺服器的資料。可見,hash一緻性算法滿足了單調性原則。

那麼hash一緻性算法有何缺點呢?

假如現在有三台伺服器A、B和C,通過計算,A和B在hash環上位置比較近,B和C,C和A距離比較遠。那麼此時,順時針落在C伺服器和A伺服器的資料機率就會變大。落在B伺服器上的機率就小。這就出現了 資料傾斜 的問題。不能均勻配置設定資料。下圖也是資料傾斜問題的一個展現:

由Redis Cluster叢集引發的對幾種算法的思考

三、hash槽位算法

針對一緻性hash算法資料傾斜的問題,Reids Cluster進行了優化,衍生出了hash槽位算法。下面看是如何實作的。

redis叢集中,有固定的槽位數:16384。redis會根據叢集master數量,平均配置設定給每個master節點一定數量的槽位。redis會根據key進行hash計算,并對16384進行取模,得到的結果就是槽位數。這個槽位配置設定給了哪個伺服器,那麼這條資料就存放到哪個伺服器上。

當發生redis叢集擴容時,叢集加入新節點後,需要執行reshard指令,進行重新hash配置設定。此時,redis會讓使用者輸入配置設定新節點個數。一般就是16384個槽位/主節點數得到的值,對資料進行平分。選擇平分後,是之前的節點的每個節點,分一些key出來,給到新節點,來湊夠新節點的個數。因為redis的槽位總數是固定16384個,新加一個節點,rehash一次後,槽位數和節點的對應關系肯定會發生變化。就是原有節點拿出一部分槽位來,分給新加入節點。

因為新加入節點槽位是其他節點勻過來的,是以,其槽位數不是連續的,而是一段一段的。為何是其他節點勻過來,而不是全部重新配置設定一遍槽位呢,因為之前的節點已經存入資料了,如果全部重新配置設定,那麼已經存入的key還需要重新整理,是以優先配置設定沒有存入key值的槽位到新節點。

redis叢集縮容就是将删除的槽位,平均配置設定給其他master節點來接收資料。

在redis cluster叢集中,相當于是節點上放的是槽位,槽位裡放的是資料。通過平均配置設定節點上的槽位,來避免一緻性hash中資料傾斜的問題

繼續閱讀