1. 程式人生 > >consistent hash(一致性哈希算法)

consistent hash(一致性哈希算法)

group 好玩 如何實現 技術 函數類 如何 一份 gic 其他

一、產生背景

今天咱不去長篇大論特別詳細地講解consistent hash,我爭取用最輕松的方式告訴你consistent hash算法是什麽,如果需要深入,Google一下~。

舉個栗子吧:

比如有 N 個 cache 服務器,需要將一個object 映射到 N 個 cache 上,我們可以用類似下面的方法計算 object 的 hash 值,然後均勻的映射到到 N 個 cache 上:

hash(object)%N

比如object是“hello”,hash(object)是100,N為3,100%3=1,這個數據會被存到第1個cache上(0、1、2三個cache)。這樣就能解決一堆數據放到N個cache上的問題。

現在有個突發情況,0、1、2三個cache中1損壞了!

怎麽辦呢,cache 1上的數據首先需要遷移,可用cache數量變為2,這個時候我們需要重新計算所有數據的hash值,上面的公式變成了這樣:

hash(object)%(N-1)

這次重新計算意味著每一份數據hash之後的結果幾乎都變了!也就是意味著在cache系統中會發生大規模cache失效,數據訪問會直接沖擊cache後面的服務器,好玩了,崩了。

咋個辦呢,consistent hash出現了!

二、算法原理

  consistent hash簡單的說,就是要在cache數量變化時,能夠盡可能小的改變已存在 key 映射關系,也就是增加一個cache或者減少一個cache服務器,對已經存在的緩存數據不要產生大影響。怎麽做到呢,,,

  我們先想象一條鏈子,第一個格子是0,最後一個格子是2^32-1,這條鏈子就是一個地址空間為2^32的hash值空間,現在將鏈子首位相連,組成一個ring,就像下面這樣(這裏畫圖太麻煩了,下面的環環相關的圖來自互聯網,侵刪吧):

技術分享圖片

  然後考慮我們由object1~object4這樣四個數據,通過hash算法後映射到這個ring上【類似這樣:hash(object1)=key1】

技術分享圖片

  這裏應該很好理解,4個數據得到4個key,接下來我們要將cache服務器也映射到這個ring上,假設由3個cache服務器,分別是cache A、cache B、cache C,然後用hash算法:hash(cache A)=key A,將3個cache服務器丟到這個ring上,就可用得到如下結果(計算cache服務器的hash值時可以用其ip等信息):

技術分享圖片

  然後順著這個ring,收集object數據,遇到cache的時候就丟進去,這樣就可以將數據和cache對應起來,於是我們可以得到如下結果:

object1:Cache A

object4:Cache B

object2/object3: Cache C

  ok,這樣就完成了數據映射,然後我們考慮一下前面普通hash算法遇到的cache服務器數量變化的情況,看一下ring hash是否解決了這個問題。

  假設cache B掉線

技術分享圖片

  這個時候可以發現只有原先映射到Cache B上的object4收到了影響,需要轉移到Cache C上。

  如果增加一個Cache結點呢?假設增加一個Cache D:

技術分享圖片

  這個時候只需要將object2轉移到Cache D上,其他的沒有變化。是不是很神奇?

  這裏可能大家會意識到一個問題,當cache服務器數量少的時候,這個算法很容易導致數據分布不均勻,所以ring hash中還有一個虛擬節點的概念,前面只剩下cache A和cache C的例子中A上有1個數據,而C中存了3個,我們將cache(假設用ip值表示)加上一個編號,然後進行hash運算,得到更多的虛擬cache結點:

hash("192.168.0.1#1")

hash("192.168.0.1#2")

hash("192.168.0.2#1")

hash("192.168.0.2#2")

  類似上面這樣,一個cache服務器就對應了2個hash值,這樣我們再丟到ring上就會得到如下結果:

技術分享圖片

  這時候object1就會落在Cache A2上,object2會落在Cache A1上,物理上其實A1和A2都是cache A,通過這種方式直觀看我們將原先的1、3分布變成了2、2分布(A上1個數據C上3個數據變成A上2個數據B上2個數據)

  引入“虛擬節點”後,映射關系就從 { 對象 -> 節點 } 轉換到了 { 對象 -> 虛擬節點 },於是乎這個算法的平衡性就更好了。

  ok,一致性hash算法的原理就介紹到這裏,下面可以看groupcahce中的consistent hash如何實現了。

三、groupcache中的consistent hash

  終於要看代碼了!!!

  源碼主要就如下幾個package,今天我們看一下第一個package:【package consistenthash】的內容

技術分享圖片

技術分享圖片

  從上圖我們可以看到,這裏只需要關註consistenthash.go這源碼文件,裏面有2個類型:Hash和Map,1個函數New,Map類型有4個不可導出的屬性以繼3個綁定的方法。

下面一個個看吧~

  1、類型Hash(需要記得Hash是一個函數類型哦)。

技術分享圖片

  2、Map類型(Map類型的第一個屬性就是上面的Hash類型變量hash,replicas屬性表示的是副本數,還記得上面我們提到的為了解決平衡性問題而引入的虛擬節點的概念嗎?這些虛擬節點這是這裏描述的副本數)。

技術分享圖片

  3、New()函數(這個函數明顯就是用來獲取上面的Map類型變量實例的,初始化副本數、hash函數、hashMap表,hashMap表的key是一個表示cache服務器或者副本的hash值,value為一個具體的cache服務器,這樣就完成了Cache A、Cache A1、Cache A2等副本全部映射到Cache A的功能。

技術分享圖片

  4、IsEmpty()函數(這個函數就沒啥可講的了,非空判斷)

技術分享圖片

  5、Add()函數(將緩存服務器加到Map中,比如Cache A、Cache B作為keys,如果副本數指定的是2,那麽Map中存的數據是Cache A#1、Cache A#2、Cache B#1、Cache B#2的hash結果)

技術分享圖片

  6、Get()函數(這個函數相對復雜一點,比如有一個數據:name="張三"這條數據需要存,這時候通過這個函數選擇一個cache服務器,返回的string類型可以是服務器ip,比如:"192.168.0.1",從而調用者能夠將name="張三"存到"192.168.0.1"上)

技術分享圖片

  ok,講完了,今天的內容有點多,可能需要多花點時間消化一下~

  下一講我們介紹groupcachepb這個package,當然不可避免地要介紹一下Protocol Buffers了,行,今天就講到這裏!

技術分享圖片

consistent hash(一致性哈希算法)