一致性Hash算法背景
一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似。一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得DHT可以在P2P环境中真正得到应用。
假设有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用类似下面的通用方法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache
hash(object)%N
一切都非常的正常,假如有一下的两种场景:
- 一个 cache 服务器
m
down 掉了(在实际应用中必须要考虑这种情况),这样所有映射到 cache m 的对象都会失效,这时候,需把 cache m 从 cache 中移除,这时候 cache 是 N-1 台,映射公式变成了 hash(object)%(N-1) ,数据丢失。 - 由于访问加重,需要添加 cache ,这时候 cache 是 N+1 台,映射公式变成了 hash(object)%(N+1)
注意,此时映射公式的取模模量改变,意味着之前所有的cache都失效了。对于服务器而言,需要对原来所有的cache迁移到新的服务器中。
- 硬件能力越来越强,可能想让后添加的节点负载多一些,显然上面的 hash 算法完成不了。
有什么可以改变,Consisten hashing
可以解决。
hash算法和单调性
Hash 算法的一个衡量指标是单调性(Monotonicity
),定义如下:
单调性是指如果已经有一些内容通过哈希分派到了相应的服务器中,又有新的服务器加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的服务器中去,而不会被映射到添加之前的服务器集合中的其他服务器中。
显然,hash(object)%N
无法达到要求。
Consistent Hashing 一致性hash的原理
consistent hashing
是一种hash算法,通俗的说,在移除或者是添加一台服务器的时候,使以尽可能小的数据迁移代价去改变原有的key
映射关系,从而去满足单调性的要求。
如何做到?先从结构开始谈起。
1.环形hash空间
考虑通常的 hash 算法都是将 value 映射到一个 32 位的 key 值,也即是 0~2^32-1 次方的数值空间。我们就可将其看作为首尾相接的环。如下图1:
2.把需要缓存的内容通过hash计算映射到hash空间
假设有四个对象object1~object4,通过hash函数计算的hash值key在环上的分布如图2所示。
实际可能不是这么均匀
3.把服务器(节点)映射到hash空间
Consistent hashing
的基本思想就是将对象和 服务器(节点)都映射到同一个 hash 数值空间中,使用是同种的 hash算法。
假设当前有 A,B 和 C 共 3 台服务器(节点),其映射结果将如图 3 所示,他们在 hash 空间中,以对应的 hash 值排列。
一般可以通过服务器的ip,mac地址…作为hash输入。实际并上不会这么均匀:
hash(M_A) = key A;
hash(M_B) = key B;
hash(M_C) = key C;
4.把对应的对象映射到节点
现在服务器(节点)和对象都通过相同的hash算法映射到hash数值空间中。那么,如何将对象映射到服务器(节点)上。
使用的策略是:将当前对象节点,按照顺时针方向出发,第一个遇到的节点,就是当前对象要存储的节点。由于对象和节点的hash值是固定的,所以节点必然是唯一和确定的。上图3所示。
所想可以对所有的节点hash值进行排序,假设还是上面的三个节点,排序之后的序列,我假设为se_M = [m2 , m1 , m3 ]
, 通过前端负载的服务器,每个前端的服务器都保存有序列se_M
,对每次来临的请求对象进行hash值计算,使用二分法在序列中查找到首个大于或等于
当前对象hash值的节点,将对象存储到节点中,就可以实现将对应的对象映射到对应的节点中。
5.服务器(节点)变动情况
文章开始提到通过hash求余
计算出节点的最大问题在于无法满足单调性,当节点有所变动时,节点中的数据有可能丢失(节点down),或者是数据迁移代价过高(新增节点),来直接的对后台服务器进行冲击。
consistent hashing
如何解决?
假设此时M_B
突然down
了,根据上面的对象映射到节点的方法,此时受影响的仅仅是沿M_A
到M_B
顺时针路径上的对象。也就是本来应该映射到M_B
上的对象。
考虑添加一台新的节点M_D
的情况,假设在这个环形 hash 空间中, M_D
被映射在对象 object3 和object4 之间。这时受影响的将仅是那些沿 M_A
顺时针直到下一个 M_D
之间的对象(它们是也本来映射到M_B
上对象的一部分),将这些对象重新映射到 M_D
上即可,如图5.
上述这个结构还存在非常大的问题。首先,根据hash函数的性质易知样本量在比较大的情况下,才体现出均分的性质,样本量小时并不保证均分。于是上面的环就不可能被均匀的拆分。其次,即使你可以通过某种蜜汁手段来保证开始的时候能够被均分,在节点增减的过程中,均分的结构又被破坏了。
如果能解决上述问题,一致性hash算法就既能做到
数据迁移的代价很低
又能够做到
负载均衡
.
6. 虚拟节点
虚拟节点技术可以完美的解决上述两个问题。以上述中三个节点M_A
,M_B
,M_C
为例子,摒弃直接以节点中ip,mac等等计算hash值的方法去映射环中的位置。而是给M_A
,M_B
,M_C
分别分配数量比较大的虚拟节点,然后,通过分别计算虚拟节点的hash值,来将各个节点中的虚拟节点分配到环中对应的位置,这样通过比较大的样本量,通过hash函数性质易知,就能够达到每个节点对应的虚拟节点在环中均分的目的。
对于虚拟节点和物理节点的问题,可以利用一个路由表结构,将物理机和虚拟节点一一对应起来,在环中对象映射到虚拟节点时,通过路由表查询到对应的物理节点,对象映射到虚拟节点的操作实质上变成了对象映射到虚拟节点所对应的物理节点上。
这样就解决了在减少或者是增加节点的时候,造成环的不均分问题。
【可能带来的问题】:可能虚拟节点过多,会造成虚拟节点重叠问题。
小概率事件
参考博文:
百度百科_)一致性哈希
https://www.cnblogs.com/lpfuture/p/5796398.html
http://www.zsythink.net/archives/1182