分布式存储引擎大厂实战——一文了解分布式存储的可靠性

背景

  上一节分布式存储引擎大厂实战——一致性哈希在大厂的应用介绍了分布式存储中分布式哈希表(DHT)是如何解决数据分布震荡的。而本章将聚焦于分布式存储后是如何保证数据可靠性的。可靠性可以说是存储系统的重要指标之一。常规情况下,一般采取存储多个副本来提高存储系统的高可靠性,多副本就是把数据复制成多份并分别存储到不同地方以实现冗余备份。仅有理论上来说数据存的份数越多,可靠性也越高,但是可用空间也在随之减少。举个例子,100T的空间如果按2副本来存数据,那么只能存50T的数据(50T2=100T),现在副本数加大,改为存5副本,那么久只能存20T的数据了。数据想要不丢失就得保存多份,但是保存多份副本又会造成空间利用率又低,所以,两者之间要做个权衡。在软件系统的高可靠性(也称为可用性,英文描述为HA,High Available)里有个衡量其可靠性的标准——X个9,这个X是代表数字3~5,X个9表示在软件系统1年时间的使用过程中,系统可以正常使用时间与总时间(1年)之比,按照业界典型的5个9的可靠性要求,(1-99.999%)3652460=5.26分钟,即系统运行一年时间里业务中断时间最多不超过5.26分钟。一般来讲把副本保存为3副本。

盘级可靠

  什么叫盘级可靠?说白了,以N副本为例,就是故障了N-1块盘,数据依然还能正常获取到。按照这个要求看3副本,其实就要求数据的3个副本必须跨盘存储。示意图如下:
在这里插入图片描述

盘级容错

  如果两个盘发生了故障,依然可以拿到数据。但是由于3副本的盘都部署在同一物理节点上,假设该节点发生故障将造成3个副本都丢了,这个时候就需要节点级容错了。

节点级容错

  按照盘级容错的概念去类推的话,就是N个副本,故障N-1个节点,数据依然能够取到。做到这一点,需要副本跨节点的去存储(不能存储在一个节点上):
如下面图所示,每个蓝色框代表一个物理节点, 白色代表一个硬盘,你会发现 副本A1 副本A2 副本A3 都是分布在三个不同的节点上面
在这里插入图片描述

机柜级容错

  N个副本,随便故障N-1 个机柜,数据依然能获取到。那就要求N个副本分布在不同的N个机柜上面,如下图所示: 绿色表示一个实体机柜,蓝色是一个 实体节点, 白色是一个实体硬盘。
在这里插入图片描述
  通过上面三幅图,应该能发现这样的规律: 如果是机构级容灾的,那么一定满足 节点级容灾 以及盘级容灾,如果是节点级容灾一定满足盘级容灾
  我们总结下,上述内容概述了三种容灾的数据存储方式,那么实际在存储的IO过程中如何做到这个可靠性的?由于存储是要求要有高并发能力的,因此用分布式事务是不合适的,接下来我们讲一种结合。

乐观锁实现的可靠性算法

  分布式系统的本地操作和数据交换当作事件,理想状态下,事件时间上的先后关系和因果关系如果是全序的为了解决分布式系统的全序关系,则系统中的进程按照全序关系执行处理事件,很容易达成一致。实际的情况是,进程存在空间隔离、时钟漂移、网络延迟等原因导致事件的全序关系难以确定。为了解决这个全序问题,采用Vector Clock.
  采用Quorum NRW模型:N: 复制的节点数量,R: 成功读操作的最小节点数,W: 成功写操作的最小节点数,仅仅需W + R > N。就能够保证强一致性。以三副本为例,NRW= 3:2:2。 这样一定能保证读的最新的数据。

写操作流程

  客户端从三副本的三元组里面跟主通信,主在接受到写请求后会把写同步写到两个备, 只要 主 + 1个备成功,则对客户端就返回成功了,另一个备是否成功不影响对外部返回写成功。
在这里插入图片描述

读操作流程

  流程图跟上述图片类似,客户端从三副本里面读取两个副本数据,就认为读成功,假设三个副本的更新情况如下,副本1 ,副本2 写成功,副本3 写失败,这时候客户端认为写是成功的。

  这个时候来读数据,因为需要最少读两个副本数据成功才认为是成功的,这时候的读取情况3个副本随机选2,所以可能的组合只能是(副本1,副本2)(副本1,副本3)(副本2,副本3),你会发现一定能读取到副本1 或者副本2的至少一个值,所以一定会读到更新成功的数据,从而保证客户端一定能看到最新的写的数据。
在这里插入图片描述

Vector Clock实例讲解

  首先它本身是一个向量时钟,向量的每一个分量为(node:version),node即为分布式系统的节点,version为相应节点上的版本号,有写请求的时候同一个node上对应的version会递增。节点间数据同步的时候也会把 (node:version)组合的信息带上。这个时候通过比较这些向量的大小,来确定写操作的时序问题了。比如(node1:v1)对比(node1:v2)或者(node1:v1,node2:v1), 那么 (node1:v1)的数据显然是个旧的数据。

  下面,以三副本为例讲下数据写过程以及向量时钟过程:

时刻 操作 结果
1:00 客户端1向node1写数据,比如age=1 这个时候node1就有了一个时钟向量版本 node1:(nod1, v1)
2:00 客户端2向node1写数据,比如age=2 这个时候node1上面的数据更新为 node1:(node1, v2)
3:00 nod1 需要同步数据age=1到node3, node3接收到数据后之后数据就更新为node3: (node1:v2)
4:00 nod1 需要同步数据age=1到node2 node2在接受数据完成更新后,数据就变成node2:(node1:v2)
5:00 客户端3在node2上面更新了age=3 这时候node2更新成功数据后,数据变成节点node2:(node1:v2,node2:v1)
6:00 客户端4在node3上面更新了age=4 这时候node2更新成功数据后,数据变成节点node3:(node1:v2,node3:v1)

  最终各个节点数据分布情况如下

节点 数据分布
node1 (nod1:v2)
node2 (node1:v2, node2:v1)
node3 (node1:v2, node3:v1)

node1 (nod1:v2)
node2:(node1:v2, node2:v1)
node3:(node1:v2, node3:v1)

  这时候再来分析下读过程: 根据NRW规则,在NRW=3:2:2情况下,需要读两份成功读才能会成功。假如读到的是 node1 和node2 的数据 则 node2的数据一定是新的数据 ,读到的是 node1 和node3的数据,则node3的数据一定是新数据。
如果读到的是 node2 和node3的数据,则没法判断哪个更新,这时候就出现了所谓的版本冲突,这个时候需要客户端自行判断的,比如可以根据数据的时间戳做简单的判断。

Vector Clock总结如下

  • 不能解决所有场景的问题,必要时候需要客户端去做决策。
  • 向量组会随着节点的增加而增长,需要定期做合并缩短。

猜你喜欢

转载自blog.csdn.net/songguangfan/article/details/114600189
今日推荐