一致性哈希算法的原理与实现
总结:
一致性hash算法在分布式系统中得到了广泛应用,主要可以用来作为负载均衡的算法。
一致性hash算法和普通hash算法区别:
一致性hash算法和普通hash算法都是使用取模的方法,只是,普通hash算法是对服务器的数量进行取模,而一致性hash算法是对2^32取模。
一致性hash算法解决了什么问题:
由于普通hash算法是对服务器的数据进行取模,因此如果服务器增加或减少,势必会造成之前hash出来的数据有问题,
这就需要重新去对新的服务器数量进行取模,而一致性hash算法就解决了这种问题。
一致性hash算法因为有可能造成数据倾斜情况,就是数据分配不均匀。那么,可以使用虚拟节点来进行解决。
因为节点数越少,越容易出现节点在哈希环上的分布不均匀,导致各节点映射的对象数量严重不均衡(数据倾斜);
相反,节点数越多越密集,数据在哈希环上的分布就越均匀。
下面文章有会介绍。
为什么hash一致性的数据空间范围是2^32次方?
因为,java中int的最大值是2^31-1最小值是-2^31,2^32刚好是无符号整形的最大值;
为什么java中int的最大值是2^31-1最小值是-2^31?
因为,int的最大值最小值范围设定是因为一个int占4个字节,一个字节占8位,二进制中刚好是32位。
原理:
1、公用哈希函数和哈希环:
计哈希函数 Hash(key),要求取值范围为 [0, 2^32)
各哈希值在上图 Hash 环上的分布:时钟12点位置为0,按顺时针方向递增,临近12点的左侧位置为2^32-1。
2、节点(Node)映射至哈希环:
如图哈希环上的绿球所示,四个节点 Node A/B/C/D,
其 IP 地址或机器名,经过同一个 Hash() 计算的结果,映射到哈希环上。
3、对象(Object)映射于哈希环:
如图哈希环上的黄球所示,四个对象 Object A/B/C/D,
其键值,经过同一个 Hash() 计算的结果,映射到哈希环上。
4、对象(Object)映射至节点(Node):
在对象和节点都映射至同一个哈希环之后,要确定某个对象映射至哪个节点,
只需从该对象开始,沿着哈希环顺时针方向查找,找到的第一个节点,即是。
可见,Object A/B/C/D 分别映射至 Node A/B/C/D。
删除节点:
现实场景:服务器缩容时删除节点,或者有节点宕机。如下图,要删除节点 Node C:
只会影响欲删除节点(Node C)与上一个(顺时针为前进方向)节点(Node B)与之间的对象,也就是 Object C,
这些对象的映射关系,按照 原理第4点 的规则,调整映射至欲删除节点的下一个节点 Node D。
其他对象的映射关系,都无需调整。
增加节点:
现实场景:服务器扩容时增加节点。比如要在 Node B/C 之间增加节点 Node X:
只会影响欲新增节点(Node X)与上一个(顺时针为前进方向)节点(Node B)与之间的对象,也就是 Object C,
这些对象的映射关系,按照 原理第4点 的规则,调整映射至新增的节点 Node X。
其他对象的映射关系,都无需调整。
虚拟节点:
对于前面的方案,节点数越少,越容易出现节点在哈希环上的分布不均匀,导致各节点映射的对象数量严重不均衡(数据倾斜);相反,节点数越多越密集,数据在哈希环上的分布就越均匀。
但实际部署的物理节点有限,我们可以用有限的物理节点,虚拟出足够多的虚拟节点(Virtual Node),最终达到数据在哈希环上均匀分布的效果:
如下图,实际只部署了2个节点 Node A/B,
每个节点都复制成3倍,结果看上去是部署了6个节点。
可以想象,当复制倍数为 2^32 时,就达到绝对的均匀,通常可取复制倍数为32或更高。
虚拟节点哈希值的计算方法调整为:对“节点的IP(或机器名)+虚拟节点的序号(1~N)”作哈希。