分散式系統基礎-一致性雜湊

weixin_34247155發表於2017-06-17

注意: 本文是讀者在理解了一致性雜湊演算法之後,寫的一些感悟.由於參考的大多數資料都是英文文獻,所以可能會有部分理解的與原文有差異.在文末附上了一些參考文獻,其中便有作者讀的Standford CS168的一篇文件,請讀者在閱讀時適當結合英文原文.當然,也有的中文資料寫的也不錯,我看過一篇.但是考慮到自己動手記錄下才能更加加深印象,所以寫成此文.文筆拙劣,當讀者們見笑了.

很早就對分散式系統感興趣,但是一直沒有深入研究,一是因為忙著做其他事情,二是並沒有什麼書籍能夠全面的介紹分散式系統中常用的演算法.

Google了一番之後,發現有人說** Amazon的Dynamo**論文中,比較詳細的介紹了分散式系統演算法.於是就打算讀這篇論文,把這篇論文中的演算法學習一下.

我們先了解一下什麼是** Dynamo**.從Wikipedia上我們可以看到其定義為:

4108852-b2139cd385edfbac.png

通俗一點的講,就是一個Key/Value儲存系統,與其類似的產品還有Consul, Etcd, Zookeeper.

論文中第一個看到的演算法就是** Consistent Hash**,中文譯法便是一致性雜湊.

那麼這個演算法的作用是什麼呢?它能容忍叢集的變動,使得即使叢集中新增或者刪除了節點,我們能夠通過最小的代價來進行調整.** Dynamo**的論文中指出,它們使用這個演算法來實現資料的分割槽和備份.

一致性雜湊的使用場景

最開始,一致性雜湊是被用在設計分散式快取系統中.關於快取,我們已經瞭解的夠多了.這裡就不詳細介紹了.

當然,其使用場景遠遠不止於快取系統.實際上,其在大量分散式系統中都有使用.

傳統演算法的不足

當設計一個分散式快取系統時,關於資料的分佈問題,最容易想到的解決方案便是,對key取Hash,轉換成一個32位的數字,然後將其mod節點的數量,決定放在哪個節點中.這種方案在叢集拓撲比較穩定時,是一個不錯的方案.但是,如果我們新增一個節點,便會是一個災難性的變化.比如,我們採用md5(key).toUInt32().mod(node_size)這個演算法來計算節點的位置,原先有100個節點,key為AAAAAAA的那個資料,原先是放在node1上的.當我們想要讀取這個key對應的資料時,我們可以通過md5("AAAAAAA").toUInt32.mod(100)這個函式來得到存在node1上,然後去對應的node1上找.現在問題來了,如果叢集中新增了一個節點,現在節點數變為了101,那我們通過md5("AAAAAAA").toUInt32.mod(101)這個函式得到的結果就可能是node2,這當然會造成miss.然而,實際上這個資料還是快取在整個叢集中的.

我們可以看到,上面的演算法的主要不足是,當新增了節點時,由於計算儲存的節點依賴於節點的數量,所以在讀快取時,會造成明明資料存在卻miss的情況.

一致性雜湊的基本思想

我們知道,在一個叢集中,可用節點的數量是一直在變得,這就導致傳統演算法的弊端被放大.基於此,Karger和其他專家提出了一致性雜湊演算法.那麼它是如何解決出現傳統的演算法中的不足呢?

在我們考試的時候,考場的門上都貼著這個考場中考生的考號的範圍.我們在進考場時,就得找到包含我們的考號的那個考場.

其實一致性雜湊的實現方法就跟這個類似.

在一致性雜湊中,我們除了需要計算要儲存的資料的key的hash之外,還要計算節點的hash,然後在儲存時,選擇一個跟key的hash最接近的節點,儲存進去.

比如說,AAAAAAA這個key的hash是100,我們還是有100個節點,其中第一個節點的hash值是10000, 第二個節點的hash值是20000,依次類推.那麼要儲存AAAAAAA這個key的節點就是第一個節點.

我們可以看到,第一個節點能夠存放從hash值為0-10000 的key,第二個節點能夠存放hash值為10001-20000的key.依次類推.

如果我們想要讀取key為AAAAAAA的資料,經過計算,從節點1中讀取即可.即使我們新增了第101個節點,還是從節點1中讀取.

4108852-75c133ed58f1390f.png

這張圖片來自史丹佛大學cs168課程的一份文件中.

上圖中的字首為s的節點,表示叢集中的一個節點,字首為x的節點,表示要被快取的資料.從第一張圓圈中,我們可以看到,由於x1, x2的hash值裡s0最近,所以被存到s0這臺節點中了,同理,x0被存到了s2這個節點中了.

在第二個圓圈中,由於多了一臺節點,s3, 而x2現在又離它最近,所以,x2需要從s0節點遷移到s3中.

從上圖中,我們也能看到,儘管一致性雜湊解決了由於叢集變動而導致的資料不命中率增加的問題,但是又引入了另一個複雜的問題,就是每個節點的負載不相同,因為每個節點的hash是根據IP計算出來的.

那一致性雜湊是如何解決這個問題的呢?

虛節點

一致性雜湊中,是通過虛節點來解決這個問題的.所謂的虛節點,顧名思義,就是虛擬的節點.那它到底是什麼呢?之前我們在介紹一致性雜湊的時候,是將物理節點直接通過雜湊運算得到其hash值,而後資料的key計算出來之後,與節點的雜湊進行比較,決定存放在哪個節點中.而現在,我們用幾個虛擬節點代表一個物理節點.不同虛擬節點的hash是通過不同的雜湊函式計算出來的.比如,上圖中的三個物理節點,如果每個物理節點有四個虛擬節點的話,經過不同的雜湊函式計算,可能得到的結果就是這樣了:

4108852-5c819e92bf84ebb1.png

這樣,通過新增虛節點的方式,就能在一定程度上平衡各個節點的負載.

參考資料

Distributed Systems Part-1: A peek into consistent hashing!

CASSANDRADATABASELABS
Consistent Hashing in Cassandra

Standford CS168 Consistency Hash

相關文章