1.背景
现在,我们有这样一个场景:我们的媒体上传服务每天都会接收海量的数据,面对这么多的海量数据,单台服务器已经不足以存储得下了,我们需要考虑水平扩容多台机器来联合存储这些海量数据。但是每台服务器又都是相互独立的,而且数据也不能乱存,万一发生存储倾斜,导致有的服务器硬盘马上爆炸🎆,而有的服务器则似闲庭信步😀😨,毫无存储压力,这怎么能行。
因此,我们就采用传统的Hash存储方式来解决这个问题:假如我们目前有3台服务器,那么当用户上传来一份数据后,我们就对其文件内容进行Hash
(举个例子而已,实际这么做,上班需要带着头盔才能去😋),然后对Hash值再模3(服务器的台数)
,最终根据计算得到的offset
来选择某一台服务器进行存储即可。
乍一听上去,这个方案仿佛解了我们的燃眉之急,但是它存在一个隐患——进行服务器水平扩缩容时,可能会大概率面临几乎全部数据都要rehash + relocate
一遍的糟糕情况。
那我们该如何解决呢?哎,这下我们就得拿出进阶版的Hash解决方案了:一致性Hash
!
2.实现方式
执行流程:
- 先根据服务器的IP或者地理位置进行
Hash
运算,然后对232
取模,得到服务器节点在Hash环
上的位置。 - 然后当前有新的数据到达时,同样对其进行
Hash
运算,然后再对232
取模,然后得到其在Hash环
上的位置。然后顺时针寻找离它挨得最近的服务器节点进行存储。
进行服务器水平扩缩容后,这里具体为增加1台服务器。
执行流程:
- 你会发现,我们仅需要迁移服务器1和服务器4这两台服务器对应的
Hash
点之间的数据点即可(因为是顺时针选择存储服务器节点),而其他的数据点动都不用动。这就是一致性Hash算法的优势所在——降低了水平扩缩容需要大规模移动数据的风险。
:::info
Q:为什么选择232
而不是其他数值?
A:
- 起初,一致性Hash算法是用在32位系统上的,使用这个数值意味着可以利用整个寻址空间。
- 很多常见的Hash函数,比如
CRC32
等,最终都会产生32
位的Hash值,这样就无需二次处理得到的Hash值了。 232
已经可以提供足够大的空间来避免Hash冲突了,因此再加容量来进一步避免Hash冲突性价比不高。232
已经提供了足够高的粒度供数据节点均匀分布在Hash环上了。- 为了兼容不同的分布式系统和存储系统,减少不必要的集成和迁移成本。
:::
3.存在的问题
数据偏斜问题
直观上来看是Hash节点分布不均,因此具体情况分为:
服务器Hash节点分布不均。
- 在这种情况下(就上图而言),node-4自己要承载接近总流量的一半的压力,这并没有达到负载均衡的目标。
- 一旦node-4宕机,那么这一半的数据节点都需要进行迁移,开销也挺大的,而且随后,node-2所存储的数据将会猛增,同时所要承载的流量也将会猛增,这个时候node-2也有可能会因此而宕机,于是乎雪崩式的的连锁反应出现了。。。显然,数据偏斜问题会给整个服务系统带来不稳定不确定的影响。
- 数据Hash节点分布不均。
解决方案
将一个实际的服务节点用多个虚拟的服务节点(vnodes)来代替,然后将虚拟的服务节点映射到Hash
环上以通过(伪)增加服务节点的方式来解决节点分布不均的问题。
你会发现,目前的分布状态下,流量到达时,都可以被均摊到每个服务节点上,同时,当某个服务节点宕机后,它的遗留下来的数据和所要承载的流量均能被其他节点所分摊而不是只有一个节点来承担,这提高了服务系统的整体的稳定性。
同时,我们也可以根据服务器的配置高低,相对应的多分配或者少分配一些虚拟服务节点,从而变相实现了多服务器下的负载均衡的权重特性。
4.优点
避免了在服务器水平扩缩容的情况下可能出现的数据大规模迁移的糟糕情况,使服务扩缩容变得更加容易便捷。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。