一、一致性哈希性质
1.平衡性:
哈希的结果尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。
2.单调性:(一致性哈希区别于其它哈希最显著的特征)
单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲区加入到系统中,那么哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲区中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
简而言之,就是添加节点之后,原有已分配的内容要么不迁移,要么就迁移到新的节点,不会迁移到旧的节点。
(线性哈希 x = (ax + b) mod § 不满足单调性,而一致性哈希满足)
3.分散性:
在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。
4.负载:
负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。
5.平滑性:
平滑性是指缓存服务器的数目平滑改变和缓存对象的平滑改变是一致的。
二、一致性哈希VS传统取模方式
1.传统的取模方式
例如10条数据,3个节点,如果按照取模的方式,那就是
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在增加节点的时候,都需要做搬迁,成本太高
2.一致性哈希方式
十条数据,算出各自的哈希值
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需要做迁移
以上例子最关键的区别就是,对节点和数据,都做一次哈希运算,然后比较节点和数据的哈希值,数据取和节点最相近的节点做为存放节点。这样就保证当节点增加或者减少的时候,影响的数据最少。
通过一致性哈希算法做数据分布,在增减节点的时候,可以大大减少数据的迁移规模。
三、注意点:虚拟节点
1.虚拟节点的由来
一致性哈希算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜问题。例如系统中只有两台服务器NodeA和NodeB,此时必然造成大量数据集中到NodeA上,而只有极少量会定位到NodeB上。为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。例如上面的情况,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “NodeA#1”、“NodeA#2”、“NodeA#3”、“NodeB#1”、“NodeB#2”、“NodeB#3”的哈希值,于是形成六个虚拟节点。同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“NodeA#1”、“NodeA#2”、“NodeA#3”三个虚拟节点的数据均定位到Node A上。这样就解决了服务节点少时数据倾斜的问题。
引入“虚拟节点”后,映射关系就从 { 对象 -> 节点 } 转换到了 { 对象 -> 虚拟节点 } 。查询物体所在 cache 时的映射关系如图所示。
2.虚拟节点的数量
x轴表示的是需要为每台物理服务器扩展的虚拟节点倍数(scale),y轴是实际物理服务器数,可以看出,当物理服务器的数量很小时,需要更大的虚拟节点,反之则需要更少的节点,从图上可以看出,在物理服务器有10台时,差不多需要为每台服务器增加100~200个虚拟节点才能达到真正的负载均衡。
本文模拟节点数量从2个增加到3个时,10万条数据在各个节点上分布的数量,并得到了在不同虚拟节点数量时的分布结果,如下面所示。结果发现,当虚拟节点数量达到300及以上时,负载比较均衡。
下面是实验结果
number of virtualNode:50
[57819, 42181]
[40885, 27768, 31347]
number of virtualNode:100
[50593, 49407]
[36139, 32876, 30985]
number of virtualNode:150
[49961, 50039]
[35724, 33693, 30583]
number of virtualNode:200
[52037, 47963]
[35320, 32438, 32242]
number of virtualNode:250
[52464, 47536]
[34813, 31587, 33600]
number of virtualNode:300
[50925, 49075]
[34102, 32399, 33499]
number of virtualNode:350
[50955, 49045]
[34065, 32797, 33138]
number of virtualNode:400
[51761, 48239]
[33488, 34263, 32249]
number of virtualNode:450
[50927, 49073]
[33296, 33774, 32930]
number of virtualNode:500
[51672, 48328]
[33465, 34116, 32419]
number of virtualNode:550
[50007, 49993]
[32269, 35021, 32710]
number of virtualNode:600
[49278, 50722]
[32327, 35072, 32601]
number of virtualNode:650
[49207, 50793]
[32066, 35230, 32704]
number of virtualNode:700
[50271, 49729]
[33178, 33886, 32936]
number of virtualNode:750
[49492, 50508]
[33411, 33368, 33221]
number of virtualNode:800
[48730, 51270]
[33274, 34277, 32449]
number of virtualNode:850
[48513, 51487]
[32911, 34625, 32464]
number of virtualNode:900
[48607, 51393]
[33212, 33871, 32917]
number of virtualNode:950
[49158, 50842]
[33610, 33256, 33134]
number of virtualNode:1000
[49593, 50407]
[33457, 33316, 33227]
哈希生成算法:通常采用MD5算法
参考代码:https://github.com/fanfish/ConsistentHash