世界最优秀的分布式文件系统架构演进之路

简介: 世界最优秀的分布式文件系统架构演进之路

孙玄:毕业于浙江大学,现任转转公司首席架构师,技术委员会主席,大中后台技术负责人(交易平台、基础服务、智能客服、基础架构、智能运维、数据库、安全、IT 等方向);前58集团技术委员会主席,高级系统架构师;前百度资深研发工程师;


【架构之美】微信公众号作者;擅长系统架构设计,大数据,运维、机器学习等技术领域;代表公司多次在业界顶级技术大会 CIO 峰会、Artificial、Intelligence、Conference、A2M、QCon、ArchSummit、SACC、SDCC、CCTC、DTCC、Top100、Strata+、Hadoop World、WOT、GITC、GIAC、TID等发表演讲,并为《程序员》杂志撰稿 2 篇。

一、前言

Hadoop 是一个由 Apache 基金会所开发的分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。

Hadoop 实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS,解决了海量数据存储的问题;实现了一个分布式计算引擎 MapReduce,解决了海量数据如何计算的问题;实现了一个分布式资源调度框架 YARN,解决了资源调度,任务管理的问题。而我们今天重点给大家介绍的是 Hadoop 里享誉世界的优秀的分布式文件系统-HDFS。

Hadoop 重要的比较大的版本有:Hadoop1,Hadoop2,hadoop3。同时也相对应的有 HDFS1,HDFS2,HDFS3 三个大版本。后面的 HDFS 的版本,都是对前一个版本的架构进行了调整优化,而在这个调整优化的过程当中都是解决上一个版本的架构缺陷,然而这些低版本的架构缺陷也是我们在平时工作当中会经常遇到的问题,所以这篇文章一个重要的目的就是通过给大家介绍HDFS 不同版本的架构演进,通过学习高版本是如何解决低版本的架构问题从而来提升我们的系统架构能力。

二、HDFS1

最早出来投入商业使用的的 Hadoop 版本,我们称为 Hadoop1,里面的 HDFS 就是 HDFS1,当时刚出来 HDFS1,大家都很兴奋,因为它解决了一个海量数据如何存储的问题。HDFS1 用的是主从式架构,主节点只有一个叫:Namenode,从节点有多个叫:DataNode。

我们往 HDFS 上上传一个大文件,HDFS 会自动把文件划分成为大小固定的数据块(HDFS1 的时候,默认块的大小是 64M,可以配置),然后这些数据块会分散到存储的不同的服务器上面,为了保证数据安全,HDFS1 里默认每个数据块都有3 个副本。Namenode 是 HDFS 的主节点,里面维护了文件系统的目录树,存储了文件系统的元数据信息,用户上传文件,下载文件等操作都必须跟 NameNode 进行交互,因为它存储了元数据信息,Namenode 为了能快速响应用户的操作,启动的时候就把元数据信息加载到了内存里面。DataNode 是 HDFS 的从节点,干的活就很简单,就是存储 block 文件块。

HDFS1 架构缺陷

缺陷一:单点故障问题(高可用)

我们很清楚的看到,HDFS1 里面只有一个 NameNode,那么也就是说如果这个Namenode 出问题以后,整个分布式文件系统就不能用了。

缺陷二:内存受限问题

NameNode 为了能快速响应用户的操作,把文件系统的元数据信息加载到了内存里面,那么如果一个集群里面存储的文件较多,产生的元数据量也很大,大到namenode 所在的服务器撑不下去了,那么文件系统的寿命也就到尽头了,所以从这个角度说,之前的 HDFS1 的架构里 Namenode 有内存受限的问题。

我们大体能看得出来,在 HDFS1 架构设计中,DataNode 是没有明显的问题的,主要的问题是在 NameNode 这儿。

三、HDFS2

HDFS1 明显感觉架构不太成熟,所以 HDFS2 就是对 HDFS1 的问题进行解决。

01、单点故障问题解决(高可用)

大家先跟着我的思路走,现在我们要解决的是一个单点的问题,其实解决的思路很简单,因为之前是只有一个 NameNode,所以有单点故障问题,那我们把设计成两个 Namenode 就可以了,如果其中一个namenode有问题,就切换到另外一个 NameNode 上。

所以现在我们想要实现的是:其中一个 Namenode 有问题,然后可以把服务切换到另外一个 Namenode 上。如果是这样的话,首先需要解决一个问题:如何保证两个 NameNode 之间的元数据保持一致?因为只有这样才能保证服务切换以后还能正常干活。

保证两个 NameNode 之间元数据一致。

为了解决两个 NameNode 之间元数据一致的问题,引入了第三方的一个 JournalNode 集群。

JournalNode 集群的特点:JournalNode 守护进程是相对轻量级的,那么这些守护进程可与其它 Hadoop 守护进程,如 NameNode,运行在相同的主机上。由于元数据的改变必须写入大多数(一半以上)JNs,所以至少存在 3 个 JournalNodes 守护进程,这样系统能够容忍单个 Journal Node 故障。当然也可以运行多于 3 个JournalNodes,但为了增加系统能够容忍的故障主机的数量,应该运行奇数个JNs。当运行 N 个 JNs 时,系统最多可以接受 (N-1) / 2 个主机故障并能继续正常运行,所以 Jounal Node 集群本身是没有单点故障问题的。

引入了 Journal Node 集群以后,Active 状态的 NameNode 实时的往 Journal Node 集群写元数据,StandBy 状态的 NameNode 实时从 Journal Node 集群同步元数据,这样就保证了两个 NameNode 之间的元数据是一致的。

两个 NameNode 自动切换

目前虽然解决了单点故障的问题,但是目前假如 Active NameNode 出了问题,还需要我们人工的参与把 Standby NameNode 切换成为 Active NameNode,这个过程并不是自动化的,但是很明显这个过程我们需要自动化,接下来我们看 HDFS2是如何解决自动切换问题的。为了解决自动切换问题,HDFS2 引入了 ZooKeeper集群和 ZKFC 进程。

02、内存受限问题解决

前面我们虽然解决了高可用的问题,但是如果 NameNode 的元数据量很大,大到当前 NameNode 所在的服务器存不下,这个时候集群就不可用了,换句话说就是NameNode 的扩展性有问题。为了解决这个问题,HDFS2 引入了联邦的机制。

如上图所示这个是一个完整的集群,由多个 namenode 构成,namenode1 和 namenode2 构成一套 namenode,我们取名叫 nn1,这两个 namenode 之间是高可用关系,管理的元数据是一模一样的;namenode3 和 namenode4 构成一套 namenode,假设我们取名叫 nn2,这两个 namenode 之间也是高可用关系,管理的元数据也是一模一样的,但是 nn1 和 nn2 管理的元数据是不一样的,他们分别只是管理了整个集群元数据的一部分,引入了联邦机制以后,如果后面元数据又存不了,那继续扩 nn3, nn4… 就可以了。所以这个时候 NameNode 就在存储元数据方面提升了可扩展性,解决了内存受限的问题。

联邦这个名字是国外翻译过来的,英文名是 Federation,之所以叫联邦的管理方式是因为 Hadoop 的作者是 Doug cutting,在美国上学,美国是联邦制的国家,作者从国家管理的方式上联想到元数据的管理方式,其实这个跟我们国家的管理方式也有点类似,就好比我们整个国家是一个完整的集群,但是如果所有的元数据都由北京来管理的话,内存肯定不够,所以中国分了34个省级行政区域,各个区域管理自己的元数据,这行就解决了单服务器内存受限的问题。

HDFS2 引入了联邦机制以后,我们用户的使用方式不发生改变,联邦机制对于用户是透明的,因为我们会在上游做一层映射,HDFS2 的不同目录的元数据就自动映射到不同的 namenode 上。

03、HDFS2 的架构缺陷

缺陷一:高可用只能有两个 namenode

为了解决单点故障问题,HDFS2 设计了两个 namenode,一个是 active,另外一个是 standby,但是这样的话,如果刚好两个 NameNode 连续出问题了,这个时候集群照样还是不可用,所以从这这个角度讲,NameNode 的可扩展性还是有待提高的。

注意:这个时候不要跟联邦那儿混淆,联邦那儿是可以有无数个 namenode 的,咱们这儿说的只能支持两个 namenode 指的是是高可用方案。

缺陷二:副本为 3,存储浪费了 200%

其实这个问题 HDFS1 的时候就存在,而且这个问题跟 NameNode 的设计没关系,主要是 DataNode 这儿的问题,DataNode 为了保证数据安全,默认一个block 都会有 3 个副本,这样存储就会浪费了 200%。

四、HDFS3

其实到了HDFS2,HDFS就已经比较成熟稳定了,但是HDFS3还是精益求精,再从架构设计上重新设计了一下。

01、高可用之解决只能有两个namenode

当时在设计 HDFS2 的时候只是使用了两个 NameNode 去解决高可用问题,那这样的话,集群里就只有一个 NameNode 是 Standby 状态,这个时候假设同时两个NameNode 都有问题的话,集群还是存在不可用的风险,所以在设计 HDFS3 的时候,使其可支持配置多个 NameNode 用来支持高可用,这样的话就保证了集群的稳定性。

02、解决存储浪费问题

HDFS3 之前的存储文件的方案是将一个文件切分成多个Block进行存储,通常一个 Block 64MB 或者 128MB,每个 Block 有多个副本(replica),每个副本作为一个整体存储在一个 DataNode 上,这种方法在增加可用性的同时也增加了存储成本。ErasureCode 通过将 M 个数据 block 进行编码(Reed-Solomon,LRC),生成 K 个校验(parity)block,这 M+K 个 block 组成一个 block group,可以同时容忍 K 个 block 失败,任何 K 个 block 都可以由其他 M 个 block算出来。overhead 是 K/M。

以 M=6,K=3 为例,使用 EC 之前,假设 block 副本数为 3,那么 6 个 block 一共 18 个副本,overhead 是 200%,使用 EC 后,9 个 block,每个 block 只需一个副本,一共 9 个副本,其中 6 个数据副本,3 个校验副本,overhead 是 3/6 = 50%。

在存储系统中,纠删码技术主要是通过利用纠删码算法将原始的数据进行编码得到校验,并将数据和校验一并存储起来,以达到容错的目的。其基本思想是将 k 块原始的数据元素通过一定的编码计算,得到m块校验元素。对于这 k+m 块元素,当其中任意的 m 块元素出错(包括数据和校验出错),均可以通过对应的重构算法恢复出原来的 k 块数据。生成校验的过程被称为编码(encoding),恢复丢失数据块的过程被称为解码(decoding)。

Reed-Solomon(RS)码是存储系统较为常用的一种纠删码,它有两个参数 k 和m,记为 RS(k, m)。如下图所示,k 个数据块组成一个向量被乘上一个生成矩阵(Generator Matrix)GT 从而得到一个码字(codeword)向量,该向量由k个数据块和 m 个校验块构成。如果一个数据块丢失,可以用 (GT) - 1 乘以码字向量来恢复出丢失的数据块。RS(k,m) 最多可容忍 m 个块(包括数据块和校验块)丢失。

它是社区最新的 HDFS3 内部的方案,需要对整个 HDFS 内部实现进行改造,包括 DataNode,NameNode 还有 DFSClient,该方案同时支持在线和离线 EC。

03、在线 EC

当前 HDFS block 的副本作为一个整体连续(contiguous)的存储在一个DataNode 上,在 locality 上具有一定的优势,特别是对于 MapReduce 这样的应用,但是这种方法不好做在线 EC。当前社区方案不以 block 为单位进行 EC,而是以 strip 为单位进行 EC(HDFS 依旧管理 Block)设计思路参考了 QFS。对于配置了 EC 的文件,客户端写入时将文件的数据切成一个个的 64KB 的 strip,相邻的 strip 发往不同的 DataNode,比如当前使用 (6,3)-Reed-solomon 编码,当前正在写的文件有 6 个 strip:strip1, strip2, strip3, strip4, strip5, strip6,那么这 6 个 strip 和相应的编码出来的 3 个校验 strip 共 9 个 strip 会并行的发往 9 个不同的DataNode。这种方式写入性能更好,但是也会对网卡出口带来一些压力,同时牺牲了 locality。如果文件大小不是 64KB * 6 的整数倍(本文例子),那么最后一个strip group 显然不是满的,这时客户端还会将最后一个 strip group 的长度记在校验块中,后续读的时候,可以根据这个长度和数据块长度还有文件长度来校验。

对于 append 和 hflush 操作,最后一个 parity strip group 很可能还没有切换成新的 strip group,这就需要 DataNode 更新最后一个 parity strip 的数据。

读操作,丢失 block 时,只需要读 9 个 DataNode 中任意 6 个 DataNode 即可修复。修复过程需要读多个 DataNode,耗费网络带宽和磁盘 IO。

04、离线 EC

该方案也支持离线 EC,可以异步的将多副本文件转成 EC 文件,通过集成在NameNode 中的 ECManager 和 DataNode 上的 ECWorker 来支持。命令行工具提供 replication 和 ec 之间的转换,可以通过命令行配置哪些文件/目录使用 EC。在转换方程中,不支持 append。

这样的话,HDFS3 靠就删码技术解决了存储浪费的问题。

五、尾声

好了,到目前为止我们看到 HDFS,三个大的版本的演进的过程,一开始是HDFS1,HDFS1 有问题,所以就有了 HDFS2,HDFS2 就是为了解决 HDFS1 架构缺陷的,同学们,我们回过头来想想,其实 HDFS1 的那些架构设计问题,是不是我们平时的项目当中也是存在的,所以以后大家在项目中遇到类似问题的时候,不妨可以参考借鉴一下 HDFS2 的解决的思路。HDFS3 是对 HDFS2 的优化,其实 HDFS3 解决的这些问题,如果自己设计过文件系统的同学(本人自己设计过分布式图片服务器)也可用类似的思路去解决系统的高可用和副本造成存储浪费的问题。

希望能给大家带来收获,同学们加油!!!

目录
相关文章
|
4月前
|
缓存 负载均衡 算法
架构详解
每个系统都有服务的上线,所以当流量超过服务极限能力时,系统可能会出现卡死、崩溃的情况,所以就有了降级和限流。限流其实就是:当高并发或者瞬时高并发时,为了保证系统的稳定性、可用性,系统以牺牲部分请求为代价或者延迟处理请求为代价,保证系统整体服务可用。令牌桶方式(Token Bucket)令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。先有一个木桶,系统按照固定速度,往桶里加入Token,如果桶已经满了就不再添加。
|
4月前
|
边缘计算 运维 区块链
【分布式】架构演进
【1月更文挑战第25天】【分布式】架构演进
|
10月前
|
架构师 Serverless
|
6月前
|
存储 Kubernetes API
k8s 基本架构
k8s 基本架构
78 11
|
9月前
|
监控 关系型数据库 数据库
【架构的介绍】
【架构的介绍】
49 0
|
9月前
|
监控 关系型数据库 MySQL
架构的介绍
架构的介绍
46 0
|
安全 架构师 数据库
架构应该如何来理解?
架构应该如何来理解?
|
开发者 微服务
架构思考
架构是一种权衡
|
架构师 关系型数据库 MySQL
架构到底是什么?
1、架构与框架的区别 2、架构到底是什么? 3、架构的目的是什么?
架构到底是什么?
|
存储 Kubernetes 负载均衡
k8s的架构
k8s的架构
1443 0
k8s的架构