介绍
一致性Hash算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot Spot)问题,初衷和CARP十分相似。一致性Hash修正了CARP使用的简单哈希算法带来的问题,使得分布式哈希(DHT)可以在P2P环境中真正得到应用。
一致性Hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义:
- 平衡性(Balance):平衡性是指哈希的结果能够尽可能分布在所有的缓冲(Cache)中去,这样可以使得所有的缓冲空间得到利用。很多哈希算法都能够满足这一条件。
- 单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应该能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会映射到旧的缓冲集合中的其他缓冲区。
- 分散性(Spread):在分布式环境中,终端有可能看不到所有的缓冲,而只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上去,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应该能够尽量避免不一致的情况发生,也就是尽量降低分散性。
- 负载(Load):负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射到不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。
Hash+取模
在分布式集群中,对机器的添加删除,或者机器故障后自动脱落集群这些操作是分布式集群管理最基本的功能。如果采用常用的hash(object)%N算法,那么在有机器添加或者删除后,很多原有的数据就无法找到了,这样严重的违反了单调性原则。
sh(World) = 200; 200%4 = 0 再次读取, key对应的节点发生了变化直接导致数据缓存命不中
示例: 3个节点的集群,10条数据
0:192
1:196
2:200
3:204
4:208
5:212
6:216
7:220
8:224
9:228
取模得到节点分配
node a: 0,3,6,9
node b: 1,4,7
node c: 2,5,8
当增加一个节点的时候,数据分布就变更为
node a: 0,4,8
node b: 1,5,9
node c: 2,6
node d: 3,7
通过示例可以看出,数据3,4,5,6,7,8,9在增加节点的时候,都需要做搬迁,成本太高。
一致性hash算法
- hash值一个非负整数,把非负整数的值范围做成一个圆环;
- 对集群的节点的某个属性求hash值(如节点名称),根据hash值把节点放到环上;
- 对数据的key求hash,一样的把数据也放到环上,按顺时针方向,找离它最近的节点,就存储到这个节点
示例: 3个节点的集群,10条数据
0:192
1:196
2:200
3:204
4:208
5:212
6:216
7:220
8:224
9:228
有三个节点,算出各自的哈希值
node a: 203
node g: 209
node z: 228
这个时候比较两者的哈希值,如果大于228,就归到前面的203,相当于整个哈希值就是一个环,对应的映射结果:
node a: 0,1,2
node g: 3,4
node z: 5,6,7,8,9
这个时候加入node n, 就可以算出node n的哈希值:
node n: 216
加入节点后的数据迁移:
node a: 0,1,2
node g: 3,4
node n: 5,6
node z: 7,8,9
通过示例可以看出,这个时候只有5和6需要做迁移,通过这种算法做数据分布,在增减节点的时候,可以大大减少数据的迁移规模。
集群的节点一定会均衡分布在环上吗?
不一定,哈希有倾斜