1. 程式人生 > >深入解讀快取(二)——一致性Hash演算法

深入解讀快取(二)——一致性Hash演算法

上一篇文章中,我們已經介紹了,分散式快取的叢集,與應用伺服器的叢集策略有所不同。分散式快取叢集,每一個節點上快取的資料各不相同。

快取策略

常見的策略有求留餘數法和一致性Hash演算法。

快取的本質是一個記憶體Hash表,網站應用中,資料快取以一對Key、Value的形式儲存在記憶體Hash表中。

計算KV對中Key的HashCode對應的Hash表索引,可快速訪問Hash表中的資料。我們可以理解為:HashCode,就是該物件的唯一標識。

求留餘數法

求留餘數法,是最簡單的一種計算策略。它的計算過程,就是使用Hash表資料長度對HashCode求餘數,將餘數作為索引,使用該餘數,直接設定或者訪問快取。


舉個例子:將所有應用伺服器依次進行編號,如現在有8臺快取伺服器,那麼,我就用物件的HashCode,除以快取伺服器個數8,然後將相應的快取,存入相同的快取伺服器。取得過程與存的過程完全類似。

由於HashCode具有隨機性,因此使用求留餘數路由演算法可以保證快取資料在整個Memcached伺服器叢集中比較均衡的分佈。

對該方法稍加改進,就可以實現和負載均衡演算法中加權負載均衡一樣的加權路由演算法。可以說,簡單的求留餘數法基本上能夠滿足絕大多數的路由需求。

然而,在分散式快取叢集需要擴容時,事情就變棘手了。

如果現在增加一臺快取伺服器,變成9臺了。之前是用快取物件的HashCode,除以8,現在改成除以9了。很明顯,之前快取的資料,絕大多數都訪問不到了,絕大多數的資料,都需要從資料庫中,重新載入。

這不是我們想看到的,能不能通過改進路由演算法,使得新加入的伺服器不影響大部分快取資料的正確命中呢?現在比較流行的一種方式就是一致性Hash演算法。

一致性Hash演算法

一致性Hash演算法通過一個叫做一致性Hash環的資料結構,實現KEY到快取伺服器的Hash對映。


演算法過程如下:

先構造一個0到232整數環,然後將伺服器節點的Hash值,放在該環上(可以理解為將你的ip做hash,將ip的HashCode放在環上)。然後根據需要快取的資料的Key,計算Key的HashCode,然後在環上,順時針查詢距離這個Key的Hash值最近的快取伺服器的節點,然後將Value,儲存到該伺服器節點上。

這是當快取伺服器叢集需要擴容的時候,只需要將新加入的節點的HashCode,放入一致性Hash環中,由於Key是順時針查詢距離最近的節點,因此,新加入的節點隻影響整個環中的一小段。

請參見上圖,如果我們新加入的伺服器節點Node3,在Node1和Node2之間,如下圖:


那麼受影響的區域,只是Node2到Node3之間(順時針)的快取,此區間的快取資料,加入節點之前是快取在Node1上的,現在需要重新快取到Node3上面。

具體應用中,這個長度為232整數環,通常使用二叉查詢樹實現,Hash查詢過程實際上是在二叉樹中查詢不小於查詢樹的最小值。當然,這個二叉樹的最右邊的葉子節點和最左邊的葉子節點相連線,構成環。

通過上面的一致性Hash演算法,就解決了分散式快取叢集中擴容的問題。然而上面的方法,仍然有問題。我們已經說過,上面的架構,隻影響了Node2到Node3之間的區域(順時針),那麼Node3,也只是分攤了Node1節點的壓力,而Node0和Node2的訪問壓力,並沒有變化。也就是說:我們加入了硬體投入,卻起到了很小的效果。

一致性Hash演算法的進化版

所以說,我們還需要對上面的做法,進行改進。

上述問題是,一致性Hash演算法帶來的負載均衡不均衡。我們可以通過增加虛擬層來實現。

我們將每臺快取伺服器,虛擬為一組虛擬快取伺服器,將虛擬伺服器的Hash值,放置在Hash環上,Key在環上先找到虛擬伺服器節點,再得到物理伺服器的資訊。


這樣,一臺伺服器節點,被環中多個虛擬節點所代表,且多個節點均勻分配。很顯然,每個物理節點對應的虛擬節點越多,各個物理節點之間的負載越均衡,新加入物理伺服器對原有的物理伺服器的影響越保持一致。

本文主要介紹了快取的存取策略,最常用的、也是目前最流行的就是一致性Hash演算法,以及一致性Hash演算法的改進版的介紹。這些東西都偏理論,下篇文章,我們將從實現的角度,從具體角度來操作一下。