了解一致性HASH算法

Hash取余算法

适合在项目初期使用流量少的时候使用。

比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用类似下面的通用方法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache ,list.get(hash(object)%n);

图 1-1

如图1-1中,有3个cache,根据对hash取余使对象均匀分布的缓存中,项目初期使用是完全没问题的,用了一段时间需要增加一台cache,由于N的变化导致未命中缓存而去查了db,造成缓存雪崩。那未命中的比例为多少呢?当3台扩充1台时,会造成3/4的缓存未命中,如果99台加1台,会造成99%的缓存未命中。所以用取余算法的话,需要再半夜2点以后加机器,然后预热缓存。在这场景下取余算法问题很大,但取余法在别的场景有不错的表现,比如hashMap,nginx hash负载策略。

图 1-2

一致性Hash

为了解决节点变化的问题,我们采用一致性hash算法。

1.环空间

简单来说,一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2^32-1(即哈希值是一个32位无符号整形),整个空间按顺时针方向组织。0和232-1在零点中方向重合。

2.插入节点

 然后将各个服务器使用Hash进行一个哈希,具体可以选择服务器的ip或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置,这里假设将上文中3台服务器使用ip地址哈希后在环空间的位置如下:

3.插入对象

接下来使用如下算法定位数据访问到相应服务器:将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器。

  例如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:

4.服务扩容

回到开始提到的扩容问题,现在增加cache4,哈希值在cache1,cache3之间,则节点变化如下:

增加cache4后则cache3到cache4的数据由cache1迁移到了cache4,不管之前有多少节点,增加一个节点时只会影响1个节点的部分数据。

综上所述,一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

5.虚拟节点

虽然解决了扩容问题,到一致性哈希算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜问题。例如系统中只有两台服务器,其环分布如下,

 此时必然造成大量数据集中到cache1上,而只有极少量会定位到cache2上。为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。生成vcache1,vcahce2两个虚拟节点对应原来的cache1,

环中存放4个虚拟节点用于解决哈希倾斜的问题(实际应用中建议每个节点创建150个虚拟节点),对象找到虚拟节点后,根据映射关系找到真实服务器。

通过这篇文章是否对一致性hash有初步的理解呢,在实际编程中还有有一些其他问题,比如hash的重写保证节点生成的hashcode足够散列,hash冲突的解决,环的数据结构选择等。将在下篇java的实现代码中分析。

猜你喜欢

转载自blog.csdn.net/songzukang/article/details/82494562