参考:
https://blog.csdn.net/qq_34262582/article/details/80326189
https://blog.csdn.net/moakun/article/details/79927907
https://juejin.im/post/5ae1476ef265da0b8d419ef2
https://blog.csdn.net/kefengwang/article/details/81628977
先说结论:
一致性哈希也是哈希算法的一种,只是为了解决普通哈希因为节点的变动导致数据迁移过大的问题。其核心思想是将地址空间看做一个环状结构,将节点进行哈希,使节点尽可能均匀的散落在环上。同时将数据进行哈希,也将数据均匀的散落在环上,最后,将数据全都放置在离数据顺时针方向(因为是混装结构,顺时针方向为递增方向)最近的节点上。此时如果一个节点需要删除,那么只会影响原来的放置在待删除节点的数据,此时这些数据仍然按照顺时针方向寻找下一个节点。如果增添节点,只会影响到环上新节点与新节点逆时针方向最近节点之间的数据。这种方式对比普通哈希,大大的缓解了因节点的变更导致的大范围数据迁移的问题。但是在使用一致哈希的过程中,节点的数量比较少时,会导致大量的数据堆积在某几个节点上的情况,即出现不均衡的情况,因此为了优化这种不均匀分布的问题,引入了虚拟节点的概念,将一个节点拆分成多个虚拟的节点,这多个虚拟的节点实际对应的还是一个节点。总之环中节点数量越多,越容易使数据分布均匀,同时还具有优良的伸缩性。
借用其他博客的例子:
首先可以回顾一下普通的哈希:
普通的hash是这样的,假设有3个节点,数据分别是1 2 3 4 5 6 7 8 9 10,那么用取模方法的话分布如下。
0:3 6 9
1:1 4 7 10
2:2 5 8
这种情况下如果增加一个节点的话则会变成。
0:4 8
1:1 5 9
2:2 6 10
3:3 7
移动的数据要很多,所以这里就要用上一致性hash。
对节点做hash计算,对值也做hash运算,最后把节点弄成环。把区间数值归右端点。给个其他博客的例子:
十条数据,算出各自的哈希值
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
结果:
node a: 0,1,2
node g: 3,4
node z: 5,6,7,8,9
一致性哈希的示意图如下:
例如我们有Object A、Object B、Object C、Object D四个数据对象,经过哈希计算后,在环空间上的位置如下:
根据一致性Hash算法,数据A会被定为到Node A上,B被定为到Node B上,C被定为到Node C上,D被定为到Node D上。
删除节点的情况:
如果此时Node C出现问题需要删除,那么Object C将与Object D共同添加到Node D,环中其他数据所处的节点不会发生改变。
增加节点的情况:
环中新加入新的节点Node X,则此时Object C由原来应该处于Node C更改到新加入的节点Node 中,其他的数据所处的位置不发生改变。
数据不均匀现象:
如果节点数量过少,很有可能因为删除一个节点导致大量数据聚集在某些节点处的现象,如下图所示:
此时会导致大量的数据聚集在节点A处,数据严重不均匀。
因此为了应对这种现象,引入了虚拟节点的方法,
即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器IP或主机名的后面增加编号来实现。例如上面的情况,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:
Node A#1”、“Node A#2”、“Node A#3”三个虚拟节点其实是一个真正的节点,这三个虚拟节点上的的数据全都定位到Node A上,同理Node B#1”、“Node B#2”、“Node B#3”也是如此。