一致性哈希算法你真的理解嘛?
维基百科定义
一致哈希是一种特殊的哈希算法。在使用一致哈希算法后,哈希表槽位数(大小)的改变平均只需要对 K/n个关键字重新映射,其中K是关键字的数量, n是槽位数量。然而在传统的哈希表中,添加或删除一个槽位的几乎需要对所有关键字进行重新映射。
引出
我们在上文中已经介绍了一致性Hash算法的基本优势,我们看到了该算法主要解决的问题是:当slot数发生变化时,能够尽量少的移动数据。那么,我们思考一下,普通的Hash算法是如何实现?又存在什么问题呢? 那么我们引出一个问题:
假设有1000w个数据项,100个存储节点,请设计一种算法合理地将他们存储在这些节点上。
看一看普通Hash算法的原理:
1.png算法的核心计算如下
for item in range(ITEMS):
k = md5(str(item)).digest()
h = unpack_from(">I", k)[0]
# 通过取余的方式进行映射
n = h % NODES
node_stat[n] += 1
从上述结果可以发现,普通的Hash算法均匀地将这些数据项打散到了这些节点上,并且分布最少和最多的存储节点数据项数目小于1%。之所以分布均匀,主要是依赖Hash算法(实现使用的MD5算法)能够比较随机的分布。
然而,我们看看存在一个问题,由于该算法使用节点数取余的方法,强依赖node的数目,因此,当是node数发生变化的时候,item所对应的node发生剧烈变化,而发生变化的成本就是我们需要在node数发生变化的时候,数据需要迁移,这对存储产品来说显然是不能忍的,我们观察一下增加node后,数据项移动的情况:
如果有100个item,当增加一个node,之前99%的数据都需要重新移动。
这显然是不能忍的,普通哈希算法的问题我们已经发现了,如何对其进行改进呢?没错,我们的一致性哈希算法闪亮登场。
那么,一个亟待解决的问题就变成了:当node数发生变化时,如何保证尽量少引起迁移呢?即当增加或者删除节点时,对于大多数item,保证原来分配到的某个node,现在仍然应该分配到那个node,将数据迁移量的降到最低。
一致性Hash算法的原理是这样的:
2.png虽然一致性Hash算法解决了节点变化导致的数据迁移问题,但是,我们回过头来再看看数据项分布的均匀性, 但是引入一致性哈希算法后,为什么就不均匀呢?数据项本身的哈希值并未发生变化,变化的是判断数据项哈希应该落到哪个节点的算法变了。
3.png因此,主要是因为这100个节点Hash后,在环上分布不均匀,导致了每个节点实际占据环上的区间大小不一造成的。
改进-虚节点
当我们将node进行哈希后,这些值并没有均匀地落在环上,因此,最终会导致,这些节点所管辖的范围并不均匀,最终导致了数据分布的不均匀。
consist_hash_virtual因此,通过增加虚节点的方法,使得每个节点在环上所“管辖”更加均匀。这样就既保证了在节点变化时,尽可能小的影响数据分布的变化,而同时又保证了数据分布的均匀。也就是靠增加“节点数量”加强管辖区间的均匀。
另一种改进
然而,虚节点这种靠数量取胜的策略增加了存储这些虚节点信息所需要的空间。在OpenStack的Swift组件中,使用了一种比较特殊的方法来解决分布不均的问题,改进了这些数据分布的算法,将环上的空间均匀的映射到一个线性空间,这样,就保证分布的均匀性。
5.png