分布式文件系统HDFS原理篇

所谓分布式文件系统就是通过网络实现文件在多台主机上进行分布式存储的文件系统!它一般采用“客户端/服务器端”的模。

分布式文件系统的结构

如下图所示,分布式文件系统在物理结构上是由计算机集群的多个节点构成的。这些节点分为主节点(Namenode,也叫做名称节点)和从节点(Datanode,也叫做数据节点)。其中主节点负责文件和目录的创建、删除和重命名等,同时管理着从节点和文件块的映射关系,因此客户端只能访问主节点才能找到请求的文件块所在的位置,进而到相应的位置读取所需文件块。从节点负责数据的存储和读取,在存储时,由主节点分配存储位置,然后由客户端把数据直接写入相应从节点;在读取时,客户端从主节点获取从节点和文件块的映射关系,然后就可以到相应位置访问文件块。注意,从节点也可以根据主节点的命令创建、删除数据块和冗余复制。

补充一点,在我们日常的操作系统中,文件系统一般以512字节为一个磁盘块来存放数据,它是文件系统读写操作的最小单位,文件系统的块一般是磁盘块的整数倍,即读写是磁盘块的整数倍。HDFS中文件也是分块存储,只是它的默认大小是64MB,而且如果一个文件小于一个块的大小时,它并不占用整个数据块的存储空间。

还有需要说明的是,为了实现数据的完整性,也就是整个集群中的节点可能会故障,为了保证完整性,我们通常会采用多副本存储,也就是将同一份数据的副本存储在不同的Rack上。

分布式文件系统HDFS原理篇

HDFS的相关概念

上面已经介绍了HDFS的文件是才分为块来存储的,而且每块的大小是64MB。那么它为什么要这么大呢?要知道我们的操作系统中文件系统才512KB而已!这样做的目的是为了最小化寻址开销和数据的定位开销。当客户端需要访问一个文件时,首先从主节点获得组成这个文件的数据块的位置列表,然后根据位置列表获得实际存储各个数据块的从节点的位置,最后从节点根据数据块信息在本地文件系统中找到相应的文件,并把数据返回给客户端。这样当块比较大时,可以降低单位数据的寻址开销!当然,块也不宜太大,因为通常MapReduce中的Map任务一次只处理一个块中的数据,如果启动的任务太少,就会降低并行处理的速度。

HDFS采用抽象块的好处如下:1、支持大规模文件存储;2、简化系统设计;3、适合数据备份。

主节点和从节点

主节点负责管理分布式文件系统的命名空间,保存两个核心的数据结构,即FsImage和EditLog。如下图所示。

分布式文件系统HDFS原理篇

FsImage用于维护文件系统树以及文件树中所有的文件和文件夹的元数据,操作日志文件EditLog中记录了所有针对文件的创建、删除、重命名等操作。主节点记录了每个文件中各个块坐在的从节点的位置信息,但是并不持久化存储这些信息,而是在系统每次启动时扫描所有数据节点重构得到这些信息。

主节点在启动时,会将FsImage的内容加载到内存中,然后执行EditLog文件中的各种操作,使得内存中的元数据保持最新。这个操作完成以后,就会创建一个新的FsImage文件和一个空的EditLog文件。主节点启动成功并进入正常运行以后,HDFS的更新操作都会被写入到EditLog,这是因为对于分布式文件系统而言,Fsimage文件通常都很庞大,如果所有的更新操作都直接往Fsimage文件中添加的话,那么系统会变得非常缓慢。

从节点是分布式文件系统HDFS的工作节点,负责数据的存储和读取,会根据客户端或者主节点的调度来进行数据的存储和检索,并且向主节点定期发送自己所存储的块的列表。每个从节点的数据会被保存在各自节点的本地文件系统中。

第二主节点

在主节点运行期间,HDFS会不断发生更新操作,这些更新操作都是直接被写入到EditLog文件,因此EditLog文件也会逐渐变大。在主节点运行期间,不断变大的EditLog不会对系统性能产生影响,但是当主节点重启时,需要将FsImage加载到内存中,会逐条执行EditLog中的记录,使得FsImage保持最新。如果FsImage很大,就会导致整个过程变得非常缓慢,使得主节点在启动过程中长期处于“安全模式”,无法正常对外提供写操作,影响用户的使用。

这个时候就引入了第二主节点,首先,可以完成EditLog和FsImage的合并操作,减小EditLog文件大小,缩短主节点重启时间;其次,可以作为主节点的检查点,保存主节点中的元数据的信息。

分布式文件系统HDFS原理篇

HDFS的存储原理

数据的存储冗余

为了保证系统的容错性和可用性,HDFS采用了多副本方式对数据进行冗余存储,通常一个数据块的多个副本会被分布到不同的从节点上。

这样有以下优点,1、加快数据传输速度;2、容易检查数据错误;3、保证数据的可靠性。

分布式文件系统HDFS原理篇

数据存取策略

数据存放

为了提高数据的可靠性和系统的可用性,以及充分利用网络带宽,HDFS采用以RACK为基础的数据存放策略。一个HDFS集群通常包含多个RACK,不同RACK之间的数据通信需要经过交换机或路由器,同一RACK则不需要。

HDFS默认的冗余复制因子是3,每一个文件块会同时保存到3个地方,其中,有两份副本放在同一RACK的不同机器上,第三个副本放在不同RACK的机器上面,这样即可以保证RACK出口发生异常时的数据恢复,也可以提高数据读写能力。

数据读取

HDFS提供了一个API可以确定一个主节点所属Rack的ID,客户端通过调用API获取自己所属的Rack ID。当客户端读取数据时,从主节点获取数据块不同副本的存放位置列表,列表中包含了副本所在的从节点,可以调用API来确定客户端和这些从节点所属的Rack ID。当发现某个数据块副本的Rack ID和客户端对应的Rack ID相同时,就优先选择该副本读取数据。

数据复制

HDFS的数据复制采用了流水线复制的策略,这样大大提高了数据复制过程的效率。当客户端要往HDFS中写入一个文件时,这个文件首先被写入本地,并被切分成若干块,每个块的大小右HDFS设定的值来确定。每个块就像HDFS集群中的主节点发送写请求,主节点就返回一个可写入的从节点的列表,再写入。

数据错误和恢复

主节点出错

1、将主节点上的元数据信息同步存储到其他文件系统中;2、运行一个第二主节点,当主节点宕机以后,可以利用第二从节点来弥补,进行数据恢复。

从节点出错

每个从节点都会定期向主节点发送信息,报告自己的状态。当从节点故障时,就会被标记为宕机,这个时候主节点就不会再给它们发送IO请求。这个时候,如果发现某些数据块数量少于冗余因子,就会启动数据冗余复制,为它生成新的副本。

数据出错

客户端在读取数据后,会采用md5和sha1对数据进行校验,以确保读取到正确的数据。如果发现错误,就会读取该数据块的副本。

HDFS的数据读写过程

分布式文件系统HDFS原理篇

1、客户端通过调用FileSystem对象的open()来读取希望打开的文件。对于HDFS来说,这个对象是分布式文件系统的一个实例。

2、DistributedFileSystem通过RPC来调用namenode,以确定文件的开头部分的块位置。对于每一块,namenode返回具有该块副本的datanode地址。此外,这些datanode根据他们与client的距离来排序(根据网络集群的拓扑)。如果该client本身就是一个datanode,便从本地datanode中读取。DistributedFileSystem 返回一个FSDataInputStream对象给client读取数据,FSDataInputStream转而包装了一个DFSInputStream对象。

3、接着client对这个输入流调用read()。存储着文件开头部分块的数据节点地址的DFSInputStream随即与这些块最近的datanode相连接。

4、通过在数据流中反复调用read(),数据会从datanode返回client。

5、到达块的末端时,DFSInputStream会关闭与datanode间的联系,然后为下一个块找到最佳的datanode。client端只需要读取一个连续的流,这些对于client来说都是透明的。

分布式文件系统HDFS原理篇

1、客户端通过在DistributedFileSystem中调用create()来创建文件。

2、DistributedFileSystem 使用RPC去调用namenode,在文件系统的命名空间创一个新的文件,没有块与之相联系。namenode执行各种不同的检查以确保这个文件不会已经存在,并且在client有可以创建文件的适当的许可。如果检查通过,namenode就会生成一个新的文件记录;否则,文件创建失败并向client抛出一个IOException异常。分布式文件系统返回一个文件系统数据输出流,让client开始写入数据。就像读取事件一样,文件系统数据输出流控制一个DFSOutputStream,负责处理datanode和namenode之间的通信。

3、在client写入数据时,DFSOutputStream将它分成一个个的包,写入内部队列,称为数据队列。数据流处理数据队列,数据流的责任是根据适合的datanode的列表要求namenode分配适合的新块来存储数据副本。这一组datanode列表形成一个管线————假设副本数是3,所以有3个节点在管线中。

4、数据流将包分流给管线中第一个的datanode,这个节点会存储包并且发送给管线中的第二个datanode。同样地,第二个datanode存储包并且传给管线中的第三个数据节点。

5、DFSOutputStream也有一个内部的数据包队列来等待datanode收到确认,称为确认队列。一个包只有在被管线中所有的节点确认后才会被移除出确认队列。如果在有数据写入期间,datanode发生故障, 则会执行下面的操作,当然这对写入数据的client而言是透明的。首先管线被关闭,确认队列中的任何包都会被添加回数据队列的前面,以确保故障节点下游的datanode不会漏掉任意一个包。为存储在另一正常datanode的当前数据块制定一个新的标识,并将该标识传给namenode,以便故障节点datanode在恢复后可以删除存储的部分数据块。从管线中删除故障数据节点并且把余下的数据块写入管线中的两个正常的datanode。namenode注意到块复本量不足时,会在另一个节点上创建一个新的复本。后续的数据块继续正常接收处理。只要dfs.replication.min的副本(默认是1)被写入,写操作就是成功的,并且这个块会在集群中被异步复制,直到其满足目标副本数(dfs.replication 默认值为3)。

6、client完成数据的写入后,就会在流中调用close()。

7、在向namenode节点发送完消息之前,此方法会将余下的所有包放入datanode管线并等待确认。namenode节点已经知道文件由哪些块组成(通过Data streamer 询问块分配),所以它只需在返回成功前等待块进行最小量的复制。

《大数据技术原理与应用》