關注公衆號:xy的技術圈算法
凡是涉及到分佈式的系統,就會有負載均衡和數據分佈的問題。爲了讓鏈接(或者數據)可以分佈得更均勻,不少時候會使用到Hash算法。數據庫
那什麼是Hash算法呢?緩存
把任意長度的輸入,經過Hash算法變換成固定長度的輸出,這個輸出就是Hash值。哈希值的空間遠小於輸入的空間,因此可能會發生「哈希碰撞」,即兩個不一樣的輸入,產生了同一個輸出。服務器
Hash算法只是一個定義,並無規定具體的實現。session
具體有什麼應用場景?負載均衡
Hash算法經常使用於消息摘要的場景,開發者們或多或少據說過的MD五、SHA都屬於Hash算法的實現。分佈式
在Java語言裏,每一個Object對象都有一個hashCode方法,它默認是根據對象的內存地址計算的Hash值。固然咱們能夠覆蓋這個方法,用本身的方法去計算得出一個Hash值。函數
先Hash再取模的場景,在負載均衡中十分常見。網站
hash(request) % ncdn
假設咱們如今有3個服務器,想要作負載均衡,就能夠對請求的ip地址或者用戶的id等使用Hash函數,而後將計算得出的Hash值對3取模,餘數爲幾,就把請求分配到相應的服務器上。如圖:
這裏爲了演示方便,直接模擬用的hash後的值。
那上述方法有什麼弊端呢?
在分佈式場景下,橫向伸縮縮是一個很正常的需求。試想一下,當上述的3臺服務器須要擴展到4臺服務器時,那絕大多數請求基本上都須要從新映射到另外一個節點。由於Hash值沒變,除數變了,餘數必然會變。如圖:
這種變更有時候是不能接受的。好比在Web負載均衡的場景下,session會保存在每一個節點裏。固然,若是你是「無狀態」的服務,那不會存在這個問題。但若是是數據持久化場景,好比前面的文章裏提到的MySQL分庫分表,這樣大的變更顯然是不能接受的。由於若是增長或者刪除了一個節點,就會致使幾乎全部的數據都須要從新遷移。
無狀態服務指的是,應用不保存任何狀態。好比不用session,或者session保存在第三方緩存的節點裏。
使用一致性Hash能夠解決由於橫向伸縮致使的大規模數據變更。它是怎麼解決的呢?
前面說到用節點的數量做爲除數去求餘。而一致性Hash的除數是2^32^。從0到2^32^ - 1,首尾相連構成了一個環。咱們先對服務器節點的IP進行Hash,而後除以2^32^獲得服務器節點在這個Hash環中的位置:
如今有請求進來了,一樣進行Hash而後處於2^32^求餘。若是落在Hash環上,而後順時針找到第一個節點,這個節點就負責處理這個請求。如圖所示:
細心的讀者可能發現,一致性Hash算法在節點數量較少的時候,會出現分佈不均勻的問題。如圖所示:
這個時候,絕大多數請求都分配到了A節點,而B節點和C節點分配到的請求不多。解決這個問題的方案就是在Hash環上增長虛擬節點。實現方式也有不少種,好比:
hash(「SERVER_IP_A#1」) % 2^32^
hash(「SERVER_IP_A#2」) % 2^32^
hash(「SERVER_IP_A#3」) % 2^32^
講了這麼多,一致性Hash到底有什麼用?
除了前面提到的負載均衡、Web session和數據庫分庫分表能夠應用外,其實一致性Hash應用得最多的領域是分佈式緩存。
分佈式緩存系統通常是對Key進行Hash的。試想一下,若是直接對節點數量取模,一旦節點數量發生變化,好比新增一個節點或者刪除一個節點,那將使得幾乎全部結點的緩存失效。而若是使用一致性Hash,就只有Hash環上相鄰的節點緩存受到影響。
從Hash環和順時針查找原理不難發現,在增長一個節點後,一些原來被分配到下一個節點的請求,被分配到了新的節點。在減小一個節點後,一些原來本分配到這個節點的請求,被分配到了下一個節點。
因此不管是增長仍是減小一個節點,都只有下一個相鄰的節點會被影響而已。
認真寫文章,用心作分享。
我的網站:yasinshaw.com
公衆號:xy的技術圈