【分布式系统遨游】分布式数据存储

服务器

浏览数:16

2020-6-29

AD:资源代下载服务

今天我们来讨论分布式数据存储。

引言

由于现在我们有了很多个节点,我们就可以把之前一台机器上面的数据,打散到各个分布式集群中的节点上面去,以充分利用集群资源。其流程大概如图所示:

比如,12306网站可以将京广线的订单数据存在机器A上,京沪线的订单数据存在机器B上,这样如果用户想购买京广线的火车票,只需要从机器A查询即可,其时间复杂度相比全量查询会大大降低,解决了之前单机架构的瓶颈问题,这里每台机器上的数据是没有交集的,这就是数据分片:

那么,经过如此改进之后的存储系统,其复杂度究竟有哪些变化呢?

  • 存储数据的时候,需要选择合适的某一个集群节点来存储
  • 查询数据的时候,需要找到之前我们存储数据的那个节点

所以,其复杂度相比单机架构,多了找机器这个步骤。需要有一种算法,将我们的数据请求,导向某个特定的数据节点上,才能正确的找到我们需要的数据。在上面的例子,我们利用了一个按照业务的范围去分片的方法,将不同铁路线的数据分散到对应的机器。同时,还有哈希、一致性哈希等其他数据分片方法。那么,面对这么多种分片方案,我们究竟该如何设计和选择呢?

数据分片设计原则

在分布式数据存储中,主要的设计原则有三种:数据均匀、数据稳定、节点差异。我们来一个一个分析:

  • 数据均匀:即数据要均匀的分布在各个节点上,如果有100G的数据,有4个节点,我们希望每个节点上都有25G左右的数据
  • 数据稳定:当集群中某个节点挂掉了需要下线的时候,每个节点内部的数据不会发生大规模迁移,所有数据都要重新分布
  • 节点差异:也被称为“节点异构性”。这里的差异是指每个节点的配置可能是有差距的。有些节点配置高,性能好,那我们就要充分利用他们,给他们分配更多的数据量

这里我们注意了,数据均匀和节点差异看起来好像是冲突的,但是不是这样的。数据均匀是在节点之间配置差距不大的前提下,让数据均匀的分布。如果节点之间配置差距很大,那么还是应该以能者多劳为主。
对于业内主要的数据分片方案,有哈希和一致性哈希两种。

哈希

哈希算法在业内有非常之多的实现,其中最广为人知的一种就是取余操作。假设我们有4台机器,我们对按照如下的算法来计算:数据(可以是id) % 机器的数量(4) = 该数据所属的节点编号。在所有的数据计算完毕之后,其分布如下:

优点

我们来分析一下这种方案的优缺点。我们可以看到,当哈希函数设置得当,那么可以很好地保证所有数据都在每个节点上均匀分布,且实现较为简单。这就是它的优点。

缺点

但是假如我们其中一个节点出问题了,需要下线这个节点。下线完成之后,目前我们的机器节点数量为3。如果再次使用相同的哈希函数,对4取余,那么会造成计算错误。所以,我们需要重新计算所有的数据的分布情况,即对3取余,才能得到正确的数据分布。普通的哈希方法适用于集群中节点数量较为稳定的场景。一旦集群中的节点发生变更,会导致所有数据重新计算哈希值。
为了解决上面的问题,一致性哈希应运而生。

一致性哈希

一致性哈希,是指把节点和数据都放到一个首尾相连的环上。节点应该放在环的哪个位置上,可以根据节点的IP地址进行哈希,然后放到环对应的位置上;而数据要放在哪个节点上,则是先对数据进行哈希运算,然后找到距离运算结果最近的环上的机器节点:

图中蓝色是机器节点经过哈希运算后所在的位置,黄色是数据的哈希运算结果。针对黄色的数据节点,100、200、300、400这4条数据应该放到顺时针的第一顺位的节点上,即放到数值为400的蓝色节点上。其他的以此类推即可。

优点

一致性哈希解决了哈希算法在集群中的节点发生变更的情况下,会导致所有数据重新计算哈希值的问题。我们分析一下,为什么解决了这个问题。假设我们图中400的蓝色节点下线了,我们只需要将100、200、300、400的这几条数据,存储到下一个600的蓝色节点即可,这样只影响到了100、200、300、400这几条数据与600这个节点,并不会影响集群中的其他节点与数据。

缺点

那么这种一致性哈希方案也并不完美。我们看到,600这个蓝色节点会随着400的蓝色几点下线而变得更加劳累,会有更多的数据(100、200、300、400)被重新定位到这个600蓝色节点上,导致了数据分布不均匀的问题,长时间高负载也可能导致600这个节点也更容易发生故障,进而导致后面的900节点也发生故障,产生雪崩效应。为了解决数据分布不均匀的问题,“带有限负载的一致性哈希”方法来了。

有负载的一致性哈希

看到这个名字大家也明白了,说白了就是每个节点都有一个存储容量上限。如果超出了这个上限,就放到顺时针方向上的下一个节点即可:

假设400这个蓝色节点在添加100、200、300这几项数据之后,已经达到了存储上限,那么继续添加400这项数据,该条数据则不会像之前那样,分给400这个节点,而是会在顺时针方向上,寻找到600这个蓝色节点并存储,从而解决了某一个节点负载过高的问题,实现了一定程度上的均匀分布。

有虚拟节点的一致性哈希

以上两种方案均是基于节点之间的性能差不多的情况下,为了达到数据均匀分布的目的,产生的两种方案。而如果这些节点机器之间的性能差异很大,那么上面这两种方案就行不通了,这就是我们之前提到的“节点异构性”的问题。我们需要实现能者多劳。有虚拟节点的一致性哈希解决了这个问题。
其核心思想就是,根据每个节点的性能,为他们生成不同数量的虚拟节点。性能越强,虚拟节点的数量越多:

我们假设节点1(浅绿色)性能最差,节点2(绿色)性能一般,节点3性能最强(蓝色),那么我们就为节点1生成1个虚拟节点,为节点2生成2个虚拟节点,为节点3生成3个虚拟节点。这样,性能最强的节点3就会接受到400、500、600这几条数据,节点2和1的数据量依次减少。这个算法的本质思想就是,让性能强的节点与上一个节点的环上间隔变长,就能让更多的数据打到这个节点上。
当然,这种方案的实现比前两种更为复杂,在新增或者删除一个节点的时候,维护成本也更高。所以,我们应当按照不同的业务场景,结合集群中各节点的性能,选出一种最佳的数据分片方案。

下期预告

【分布式系统遨游】分布式复制

关注我们

欢迎对本系列文章感兴趣的读者订阅我们的公众号,关注博主下次不迷路~

作者:NoSay