問題:假設我們有 5 臺 mysql 伺服器,然後對資料庫進行水平拆分。應該怎麼做?
最簡單的做法是利用雜湊,也就是對於插入表中的資料,我們對記錄的 id 進行雜湊對映到 0~4 的區間,然後根據雜湊結果儲存到對應的 mysql 伺服器中。
這個方案乍一看問題不大,但是,在不考慮冗餘備份的情況下,我們考慮一下如果其中一台資料庫宕機了,我們的資料就會因為這種雜湊演算法而亂掉,我們就需要定義新的雜湊演算法,將雜湊對映到 0~3 的區間上,然後再對所有的資料進行重新分配,這對於分布式儲存來說是很嚴重的問題。
那針對這個問題,我們當然是不希望重新分配所有的資料,我們期望的是重新分配的資料只是宕機的那台機器上的資料就好,而且我們的儲存和獲取邏輯還不需要更換,這就是一致性雜湊的常見應用場景。接下來我們看下一致性雜湊是如何設計的。
簡化來講,我們可以認為一致性雜湊比普通雜湊表多做了一次雜湊,也就是不僅僅資料做雜湊,機器也做雜湊;
按照常用的hash演算法來將對應的key雜湊到乙個具有2^32次方個桶的空間中,即0~(2^32)-1的數字空間中。現在我們可以將這些數字頭尾相連,想象成乙個閉合的環形。
第一步:把資料通過一定的hash演算法處理後對映到環上
現在我們將object1、object2、object3、object4四個物件通過特定的hash函式計算出對應的key值,然後雜湊到hash環上。
hash(object1) = key1;
hash(object2) = key2;
hash(object3) = key3;
hash(object4) = key4;
第二步:將機器通過hash演算法對映到環上
在採用一致性雜湊演算法的分布式集群中將新的機器加入,其原理是通過使用與物件儲存一樣的hash演算法將機器也對映到環中(一般情況下對機器的hash計算是採用機器的ip或者機器唯一的別名作為輸入值),然後以順時針的方向計算,將所有物件儲存到離自己最近的機器中。假設現在有node1,node2,node3三颱機器,通過hash演算法得到對應的key值,對映到環中,其示意圖如下:
通過上圖可以看出物件與機器處於同一雜湊空間中,這樣按順時針轉動object1儲存到了node1中,object3儲存到了node2中,object2、object4儲存到了node3中。在這樣的部署環境中,hash環是不會變更的,因此,通過算出物件的hash值就能快速的定位到對應的機器中,這樣就能找到物件真正的儲存位置了。
這樣在當一台伺服器退出之後,或者有新的伺服器加入進來之後,只會影響一部分的key的雜湊分布,不至於導致所有的key的雜湊分布都失效。 以上面的分布為例,如果node2出現故障被刪除了,那麼按照順時針遷移的方法,object3將會被遷移到node3中,這樣僅僅是object3的對映位置發生了變化,其它的物件沒有任何的改動。如果往集群中新增乙個新的節點node4,通過對應的雜湊演算法得到key4,並對映到環中,通過按順時針遷移的規則,那麼object2被遷移到了node4中,其它物件還保持這原有的儲存位置。綜上所述,一致性雜湊演算法在保持了單調性的同時,還是資料的遷移達到了最小,這樣的演算法對分布式集群來說是非常合適的,避免了大量資料遷移,減小了伺服器的的壓力。
如果伺服器比較少的情況下,一般都會引入虛擬節點的概念。
虛擬節點
如上面只部署了node1和node3的情況(node2被刪除的圖),object1儲存到了node1中,而object2、object3、object4都儲存到了node3中,這樣就照成了非常不平衡的狀態。在一致性雜湊演算法中,為了盡可能的滿足平衡性,其引入了虛擬節點。
「虛擬節點」( virtual node )是實際節點(機器)在 hash 空間的複製品( replica ),一實際個節點(機器)對應了若干個「虛擬節點」,這個對應個數也成為「複製個數」,「虛擬節點」在 hash 空間中以hash值排列。
把機器按照某種hash演算法(比如md5)計算得到機器的hashcode值。
對於儲存的資料,根據資料的key,使用與機器相同的hash演算法獲取到相應的hashcode值,然後將key寫入到順時針最近的機器。
可以是hashcode(key) <= hashcode(machine)的機器。
當有新機器加入時,只需要把新加入機器影響到的資料進行重新分配;當刪除機器時,只需要把被刪除機器的資料重新分配一下,這樣可以減小資料的遷移代價。
為了維持平衡性,防止雪崩效應,使用虛擬節點代替真實機器,乙個真實機器對應多個虛擬節點,這樣可以保證資料的分布均衡
參考網上的實現:
# -*- coding: utf-8 -*-
import hashlib
class yhash(object):
def __init__(self, nodes=none, n_number=3):
""":param nodes: 所有的節點
:param n_number: 乙個節點對應多少個虛擬節點
:return:
"""self._n_number = n_number #每乙個節點對應多少個虛擬節點,這裡預設是3個
self._node_dict = dict() #用於將虛擬節點的hash值與node的對應關係
self._sort_list = #用於存放所有的虛擬節點的hash值,這裡需要保持排序
if nodes:
for node in nodes:
self.add_node(node)
def add_node(self, node):
"""新增node,首先要根據虛擬節點的數目,建立所有的虛擬節點,並將其與對應的node對應起來
當然還需要將虛擬節點的hash值放到排序的裡面
這裡在新增了節點之後,需要保持虛擬節點hash值的順序
:param node:
:return:
"""for i in xrange(self._n_number):
node_str = "%s%s" % (node, i)
key = self._gen_key(node_str)
self._node_dict[key] = node
self._sort_list.sort()
def remove_node(self, node):
"""這裡乙個節點的退出,需要將這個節點的所有的虛擬節點都刪除
:param node:
:return:
"""for i in xrange(self._n_number):
node_str = "%s%s" % (node, i)
key = self._gen_key(node_str)
del self._node_dict[key]
self._sort_list.remove(key)
def get_node(self, key_str):
"""返回這個字串應該對應的node,這裡先求出字串的hash值,然後找到第乙個小於等於的虛擬節點,然後返回node
如果hash值大於所有的節點,那麼用第乙個虛擬節點
:param :
:return:
"""if self._sort_list:
key = self._gen_key(key_str)
for node_key in self._sort_list:
if key <= node_key:
return self._node_dict[node_key]
return self._node_dict[self._sort_list[0]]
else:
return none
@staticmethod
def _gen_key(key_str):
"""通過key,返回當前key的hash值,這裡採用md5
:param key:
:return:
"""md5_str = hashlib.md5(key_str).hexdigest()
return long(md5_str, 16)
fjs = yhash(["127.0.0.1", "192.168.1.1"])
print fjs.get_node("fjs32121")
一致性雜湊
直接貼出一篇介紹的很清楚的博文。關鍵字一致性雜湊 平衡性,單調性,分散性,負載 其實說白了,就是解決把請求分散到不同的機器上運算,怎麼做分散的平均,機器少一台多一台,或者壞掉一台,成很好的自適應和拓展。最簡單的實現分布式演算法,取模嘛,但是它就上述的一些問題,所以不算好的雜湊函式。一致性雜湊演算法,...
一致性雜湊
from 學習分布式,一致性雜湊是最最基礎的知識,所以要理解好.那什麼是一致性雜湊呢?what 1.平衡性是指 hash的結果應該平均分配到各個節點,這樣從演算法上就解決了負載均衡問題.2.單調性是指 在新增或者刪減節點時,同乙個key訪問到的值總是一樣的.3.分散性是指 資料應該分散的存放在 分布...
一致性雜湊
一致性 雜湊演算法在1997年由 麻省理工學院提出 參見擴充套件閱讀 1 設計目標是為了解決網際網路中的熱點 hot spot 問題,初衷和 carp十分類似。一致性雜湊修正了carp使用的簡單雜湊演算法帶來的問題,使得dht可以在p2p環境中真正得到應用。雜湊演算法 編輯 一致性雜湊提出了在動態變...