一致性雜湊演算法

2021-06-16 08:27:21 字數 4149 閱讀 5029

好吧,我們決定打破這種基於資料項商業邏輯的劃分思維,來考慮一種基於 key 的劃分方式,這有些類似於後面介紹的資料庫水平分割槽(sharding)。我們需要設計一種不依賴資料項內容的雜湊演算法,將所有資料項的 key 均衡分配在這三颱快取伺服器上。 

乙個簡單而有效的方法是「取餘」運算,這就像打撲克時的發牌,讓所有資料項按照乙個順序在不同的快取伺服器上輪詢,這可以達到較好的相對平衡,想想發牌的動機也正是為了讓大家彼此公平。這種方法是一種比較常用的基本雜湊演算法,事實上在很多時候都用得到,而且你可以根據實際情況對它進行改造,達到更好的雜湊效果。下面我們舉個例子。 

在「取餘」之前,我們先要做一些準備工作,目的是讓 key 變成整數,而且盡量唯一。比如對於以下這個 key: 

article_090222.htm 

我們先對它進行 md5 運算,這裡直接使用 php 命令列方式: 

s-colin:~ # php -r "echo md5('article_090222.htm');" 

e6e87fc9f9c2914339a9b7cc4db6055c 

得到的是乙個 32 位元組的字串,同時它也是乙個十六進製制的長整數,為了減少計算開銷,我們取這個字串的前 5 個位元組,然後將它轉換為十進位制數: 

s-mat:~ # php -r "echo hexdec('e6e87');" 

945799 

然後將結果進行「模 3」運算: 

s-mat:~ # php -r "echo 945799 % 3;"  1 

得到的餘數便是快取伺服器的編號,我們的三颱快取伺服器應該從 0 開始編號,那麼 1 代表了第二台伺服器。 看起來真複雜,我們不得不需要乙個「快取聯結器」了,我們希望將上面這些運算都放在聯結器裡,而只需要告訴它  key,接下來選擇快取伺服器的事情就拜託給它了。看看聯結器的乙個例子: 

<?php  

function memcache_connector($key)   

?> 

現在,我們訪問快取的時候只需要這樣連線快取伺服器即可: 

<?php  

$memcache = memcache_connector('article_090222.htm'); 

?> 

那麼,如果還需要繼續擴充套件,一定難不倒你了,將「模 3」的運算變成「模 4」、「模 5」……然後其餘的工作就放心地交給聯結器去做吧。 

這裡有乙個問題也許你一直在思考,那就是當我們擴充套件快取系統後,由於分割槽演算法的改變,會涉及快取資料需要從一台快取伺服器遷移到另一台快取伺服器的問題,如何遷移呢?事實上,根本不需要考慮分割槽之間的遷移,因為這是快取,它應該具備在必要時刻犧牲自己的勇氣,當然這是你賦予它的,你必須明白快取不是持久儲存,並且從引入分布式快取開始就不斷地提醒自己。 

沒錯,當調整快取分割槽演算法後,我們需要時間來等待快取重建和預熱,但這往往並不影響站點的正常運轉,前提是你按照前面讀快取和寫快取的理念來進行設計。順便一提的是,與此相比,資料庫規模擴充套件引發分割槽(shard)之間的資料遷移就要複雜得多,後面我們會有專門的章節**它。

——郭昕《高併發web框架》

在大型web應用中,快取可算是當今的乙個標準開發配置了。在大規模的快取應用中,應運而生了分布式快取系統。

分布式快取系統的基本原理,大家也有所耳聞。

key-value如何均勻的分散到集群中?

說到此,最常規的方式莫過於hash取模的方式。比如集群中可用機器適量為n,那麼key值為k的的資料請求很簡單的應該路由到hash(k) mod n對應的機器。的確,這種結構是簡單的,也是實用的。

但是在一些高速發展的web系統中,這樣的解決方案仍有些缺陷。隨著系統訪問壓力的增長,快取系統不得不通過增加機器節點的方式提高集群的相應速度和資料承載量。增加機器意味著按照hash取模的方式,在增加機器節點的這一時刻,大量的快取命不中,快取資料需要重新建立,甚至是進行整體的快取資料遷移,瞬間會給db帶來極高的系統負載,設定導致db伺服器宕機。 

那麼就沒有辦法解決hash取模的方式帶來的詬病嗎?

一致性雜湊演算法(consistent hashing algorithm)是一種分布式演算法,常用於負載均衡。

memcached client也選擇這種演算法,解決將key-value均勻分配到眾多memcached server上的問題。它可以取代傳統的取模操作,解決了取模操作無法應對增刪memcached server的問題(增刪server會導致同乙個key,在get操作時分配不到資料真正儲存的server,命中率會急劇下降)

選擇具體的機器節點不在只依賴需要快取資料的key的hash本身了,而是機器節點本身也進行了hash運算。

(1

) hash機器節點

首先求出機器節點的hash值(怎麼算機器節點的hash?ip可以作為hash的引數吧。。當然還有其他的方法了)

然後將其分布到0~2^32的乙個圓環上(順時針分布)。如下圖所示:

集群中有機器:a , b, c, d, e五颱機器,通過一定的hash演算法,我們將其分布到如上圖所示的環上。

(2)訪問方式

如果有乙個寫入快取的請求,其中key值為k,計算器hash值hash(k), hash(k) 對應於圖 – 1環中的某乙個點,如果該點對應沒有對映到具體的某乙個機器節點,那麼順時針查詢,直到第一次找到有對映機器的節點,該節點就是確定的目標節點,如果超過了2^32仍然找不到節點,則命中第乙個機器節點。比如 hash(k) 的值介於a~b之間,那麼命中的機器節點應該是b節點(如上圖 )。

(3)增加節點的處理

如上圖 – 1,在原有集群的基礎上欲增加一台機器f,增加過程如下:

計算機器節點的hash值,將機器對映到環中的乙個節點,如下圖:

增加機器節點f之後,訪問策略不改變,依然按照(2)中的方式訪問,此時快取命不中的情況依然不可避免,不能命中的資料是hash(k)在增加節點以前落在c~f之間的資料。儘管依然存在節點增加帶來的命中問題,但是比較傳統的 hash取模的方式,一致性hash已經將不命中的資料降到了最低。

consistent hashing 最大限度地抑制了hash鍵的重新分布。

要取得比較好的負載均衡的效果,往往在伺服器數量比較少的時候需要增加虛擬節點來保證伺服器能均勻的分布在圓環上。因為使用一般的hash方法,伺服器的對映地點的分布非常不均勻。使用虛擬節點的思想,為每個物理節點(伺服器)在圓上分配100~200個點。這樣就能抑制分布不均勻,最大限度地減小伺服器增減時的快取重新分布。使用者資料對映在虛擬節點上,就表示使用者資料真正儲存位置是在該虛擬節點代表的實際物理伺服器上。

下面有乙個圖描述了需要為每台物理伺服器增加的虛擬節點。

x軸表示的是需要為每台物理伺服器擴充套件的虛擬節點倍數(scale),y軸是實際物理伺服器數,可以看出,當物理伺服器的數量很小時,需要更大的虛擬節點,反之則需要更少的節點,從圖上可以看出,在物理伺服器有10台時,差不多需要為每台伺服器增加100~200個虛擬節點才能達到真正的負載均衡。

討論:

1.一致性hash僅僅是為了不去做資料遷移,

但是隨之機器的增加會越來越不可用。而且本身的消耗也會增大

這個的依據是什麼?

你好 ,一致性hash,假設本來應該落在b點的資料,在a,b之間加一台機器,平均有一半的資料會無效。並且a到加的機器點上的資料在b上已經沒有用,怎麼去清理。隨著機器的越來越多,不命中的概率也會越來越多。 

雖然說最常用的hash取模不可避免的需要做資料遷移,但是可以選擇時間點,比如半夜兩點。這個時候訪問肯定會很少。 

不對的請指教

如果是c、a之間加入節點b,那原來落在cb之間的資料不再找a,而是找b了,這部分資料在a確實是失效。但你說的這個是純理論。實際中加入b節點之後,cb間的資料(原來命中a上)會逐漸儲存到b上(而不是不命中的時候什麼都不做),同時a上的資料隨著新到資料增加,原來那部分失效資料通過lru演算法將逐漸淘汰掉。所以我覺隨著機器增加,不命中的概率不會大幅波動。 

事實上,一致性hash就是用來解決儲存節點增加導致的命中降低問題的。 

實際例子:日本mixi也是逐漸增加到200臺以上的memcached伺服器集群,用的就是這種方法,並沒有你說的問題。

一致性雜湊演算法

在分布式系統中,如果某業務可以由多個相同的節點處理,很容易想到用hash的方式將業務請求分散到這些節點處理,如果有n個節點,計算方法為 hash id n。如果只是簡單的計算,不涉及使用者狀態,這是乙個簡單有效的方案。如果節點的計算涉及使用者狀態,比如維護購物車 memcache快取服務等,好像也沒...

一致性雜湊演算法

判定好壞的四個定義 1 平衡性 balance 平衡性是指雜湊的結果能夠盡可能分布到所有的緩衝中去,這樣可以使得所有的緩衝空間都得到利用。很多雜湊演算法都能夠滿足這一條件。2 單調性 monotonicity 單調性是指如果已經有一些內容通過雜湊分派到了相應的緩衝中,又有新的緩衝加入到系統中。雜湊的...

一致性雜湊演算法

我們的memcache客戶端 這裡我看的spymemcache的原始碼 使用了一致性hash演算法ketama進行資料儲存節點的選擇。與常規的hash演算法思路不同,只是對我們要儲存資料的key進行hash計算,分配到不同節點儲存。一致性hash演算法是對我們要儲存資料的伺服器進行hash計算,進而...