3.1 HDFS简介

随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS只是分布式文件管理系统中的一种。

3.1.1 HDFS的概念

HDFS(Hadoop Distributed File System),它是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
HDFS的使用场景:适合一次写入,多次读出的场景。一个文件经过创建、写入和关闭之后就不需要改变。

3.1.2 HDFS数据的存储和读取方式

  1. 对于大文件的存储,HDFS采用分割的方式。HDFS将大文件分割到既定的存储块(Block)中存储,并通过本地设定的任务节点进行预处理。
  2. 对于大量小文件的存储,HDFS采用普通的编码与压缩方式。在实际工作中,更多时候需要存储大量小文件。
  3. 对于普通文件的读取,HDFS通常采用分批次的方式。
  4. 对于大量数据的读取,HDFS采用集中式。存储时的优化使得读取能够在一个连续的区域内进行,节省读取数据的时间。
  5. 对于少量数据的随机读取,HDFS一般采用按序读取的方式,即先把少量的随机读取操作合并,然后按顺序读取数据。

3.1.3 HDFS的特点

  1. HDFS优点
  1. 成本低。HDFS可以部署在价格低廉的硬件上,成本较低。只要有台式机或笔记本电脑就可以部署HDFS。
  2. 高容错。HDFS利用众多服务器实现分布式存储,每个数据文件都有2个冗余备份,也就是每个数据文件被存储3份。如果存储数据的某个服务器发生了故障,数据还有2个备份,因此,HDFS具有高容错的特性,允许服务器发生故障。
  3. 高吞吐量。吞吐量是指单位时间内完成的工作量。HDFS实现了并行处理海量数据,大大缩短了处理时间,从而实现了高吞吐量。
  4. 存储数据种类多。HDFS可以存储任何类型的数据,如结构化的数据、非结构化数据、半结构化的数据。
  5. 可移植。HDFS可以实现不同平台之间的移植。
  1. HDFS的缺点
  1. 高延时。HDFS不适用于延时敏感的数据访问。
  2. 不适合小文件存取场景。对Hadoop系统而言,小文件通常指远小于HDFS的数据块大小(128M)的文件,由于每个文件都会产生各自的元数据,Hadoop通过NameNode来存储这些信息,若小文件过多,容易导致NameNode存储出现瓶颈。
  3. 不适合并发写入。HDFS目前不支持并发多用户的写操作,写操作只能在文件末尾追加数据,

3.2 HDFS组成架构

对于HDFS架构来说,一个HDFS基本集群的节点主要包括:NameNode、DataNode、SecondaryNameNode。

hdfs分布式存储 分布式文件存储hdfs_大数据

NameNode是名称节点,Master;DataNode是数据节点;SecondaryNameNode是辅助名称节点;Client是客户端。各部分的详细功能:

3.2.1 Client

Client:主要负责切分文件,与各节点进行交互。切分文件是把文件分割成数据块(Block)。数据块默认大小是128MB,每个数据块有多个副本存储在不同的机器上,副本数可在文件生成时指定(默认有3个副本)。交互包括与NameNode和DataNode进行交互,Client从NameNode获取文件的元数据,从DataNode读取数据并向DataNode写入数据。元数据是描述数据属性的数据。数据属性包括存储的位置、格式、权限、大小、历史版本等。

3.2.2 NameNode

NameNode:是HDFS架构中的主节点,管理节点(Master)。HDFS架构中只有一个NameNode。NameNode用于存储元数据以及处理客户端(client)发出的请求。元数据不是具体的文件内容,它包含3类重要信息。第1类是文件和目录自身的属性信息,如文件名、目录名、父目录信息、文件大小、创建时间、修改时间等;第2类是文件内容存储的相关信息,如文件分块情况、副本个数、每个副本所在的DataNode信息等;第3类是HDFS中所有DataNode的信息,用于管理DataNode。
在NameNode中存放元数据的文件是fsimage(文件系统镜像)文件。在系统运行期间,所有对元数据的操作均保存在内存中,并被持久化到另一个文件edits(编辑日志文件)中。当NameNode启动时,fsimage文件将被加载至内存,再对内存里的数据执行edits文件中所记录的操作,以确保内存所保留的数据处于最新的状态。
主要负责:

  1. 管理HDFS的元数据;
  2. 配置副本策略;
  3. 管理数据快(Block)映射信息;
  4. 处理Client请求。

3.2.3 DataNode

DataNode是HDFS架构中的从节点(Slave)。是真正存储数据的地方,在DataNode中,文件以数据块的形式进行存储。数据文件在上传至HDFS时将根据系统默认的文件块大小被划分成一个个数据块,Hadoop3.x中一个数据块的大小为128MB,如果存储一个大小为129MB的文件,那么文件将被分为两个数据块进行存储。再将每个数据快存储至不同的或相同的DataNode中,并且备份副本,一般默认备份3个副本,NameNode负责记录文件的分块信息,确保在读取该文件时可以找到并整合所有该文件的数据块。
主要负责:

  1. 存储Client发来的数据块,执行数据块的读写操作;
  2. 定期汇报存储信息给NameNode(发送心跳信息)。

3.2.4 SecondaryNameNode

SecondaryNameNode:定期与NameNode通信,主要用来进行镜像备份,对NameNode中的edits文件与fsimage文件进行定期合并。镜像文件是指备份fsimage文件。

fsimage和edits:fsimage(镜像文件)和edits(日志文件)是NameNode中两个很重要的文件。fsimage是元数据镜像文件,内容是NameNode启动时对整个文件系统的快照(备份)。edits元数据操作日志,内容是每次保存fsimage之后至下次保存fsimage之前进行的所有HDFS操作。

随着HDFS操作的次数越来越多,edits文件也会越来越多,所占存储空间越来越大。如果NameNode出现故障,NameNode重启时,会先把fsimage文件加载到内存中,然后合并edits文件,edits文件占空间大会导致NameNode重启很耗时。对于实际操作来说,这是一个比较麻烦的问题。SecondaryNameNode对NameNode中的edits文件与fsimage文件进行定期合并,很好解决了这个问题,这也是HDFS高容错特点的一个表现。

合并edits文件和fsimage文件,SecondaryNameNode每隔1小时执行1次,可能会出现数据丢失问题。目前大部分企业采用Hadoop的HA(High Available,高可用)模式下备用的NameNode,对NameNode中的edits文件与fsimage文件进行实时合并。

合并过程如下:

hdfs分布式存储 分布式文件存储hdfs_hdfs_02

  • 首先生成一个名叫edits.new的文件,用于记录合并过程中产生的日志信息;
  • 当触发到某一时机时(时间间隔达到1小时或Edits中的事务条数达到1百万)时SecondaryNamenode将edits文件、与fsimage文件从NameNode上下载到SecondNamenode上
  • 将edits文件与fsimage进行合并操作,合并成一个fsimage.ckpt文件;
  • 将生成的合并后的文件fsimage.ckpt文件上传到NameNode上;
  • 在NameNode上,将fsimage.ckpt变成fsimage文件替换NameNode上原有的fsimage文件。将edits.new文件变成edits文件替换NameNode上原有的edits文件。

3.3 HDFS的工作机制

3.3.1 机架感知策略和副本冗余存储策略

通常大型Hadoop集群会分布在很多机架上。在这种情况下,不同节点之间的通信希望尽量在同一个机架之内及进行,而不是跨机架;为了提高容错能力,NameNode会尽可能把数据块的副本放到多个机架上。在综合考虑这两点的基础上,Hadoop设计了机架感知(rack-aware)功能。

hdfs分布式存储 分布式文件存储hdfs_hdfs_03

在Hadoop里,以类似于一种文件目录结构的方式来表示节点。例如,H1的parent是R1,R1的parent是D1,则H1的RackID=/D1/R1/H1。其他节同理可以获取得到机架的ID。有了这些RackID信息就可以计算出任意两台DataNode之间的距离。

distance(/D1/R1/H1, /D1/R1/H1) = 0 相同的DataNode
distance(/D1/R1/H1, /D1/R1/H2) = 2 同一Rack下的不同DataNode
distance(/D1/R1/H1, /D1/R2/H4) = 4 用以IDC下的不同DataNode
distance(/D1/R1/H1, /D2/R3/H7) = 6 不同IDC下的DataNode

HDFS副本的方式策略称为机架感知策略。以默认的3个副本为例,具体策略如下:

  • 第一个副本放在本地机架的一个节点上。
  • 第二个副本放在不同(远程)机架的一个节点上。
  • 第三个副本放在同一远程机架中的不同节点上。
  • 如果还有更多的副本就随机放在集群的其他节点上。

这种策略减少了机架间的数据传输,从而提高了写操作的效率。机架故障的可能性远小于节点故障的可能性,这个策略保证了数据的可靠性。但是,它不会减少读取数据时使用的总网络带宽,因为一个块仅放置在两个唯一的机架中,而不是三个。使用此策略,块的副本不会在机架上均匀分布。两个副本位于一个机架的不同节点上,其余副本位于其他机架之一的节点上。此策略在不影响数据可靠性或读取性能的情况下提高了写入性能。
为了降低整体的带宽消耗和读取延时,HDFS会让程序尽量读取离它最近的副本。如果读取程序的同一个机架上有一个副本,那么就读取这个副本;如果一个Hadoop集群跨越多个数据中心,那么优先读取本地数据中心的副本。

hdfs分布式存储 分布式文件存储hdfs_HDFS_04

3.3.2 文件读取

HDFS文件读取过程如图:

hdfs分布式存储 分布式文件存储hdfs_hdfs分布式存储_05

  1. HDFS客户端(Client)通过DistributedFileSystem对象的open()方法打开要读取的文件。
  2. DistributedFileSystem负责向远程的名称节点(NameNode)发起RPC调用,得到文件的数据块信息,返回数据块列表。对于每个数据块,NameNode返回该数据块的DataNode地址。
  3. DistributedFileSystem返回一个FSDataInputStream对象给客户端,客户端调用FSDataInputStream对象的read()方法开始读取数据。
  4. 通过对数据流反复调用read()方法,把数据从数据节点传输到客户端。
  5. 当一个节点的数据读取完毕时,DFSInputStream对象会关闭与此数据节点的连接,连接此文件下一个数据块的最近数据节点。
  6. 当客户端读取完整数据时,调用FSDataInputStream对象的close()方法关闭输入流。

3.3.3 文件写入

HDFS文件写入过程如图所示:

hdfs分布式存储 分布式文件存储hdfs_hdfs_06

  1. 客户端调用DistributedFileSystem对象的create()方法创建一个文件输出流对象。
  2. DistributedFileSystem对象向远程的NameNode节点发起一次RPC调用,NameNode检查该文件是否已经存在,以及客户端是否有权限新建文件。
  3. 客户端调用FSDataOutputStream对象的write()方法写数据,数据先被写入缓冲区,再被切分为一个个数据包。
  4. 每个数据包被发送到由NameNode节点分配的一组数据节点的一个节点上,在这组数据节点组成的管道上依次传输数据包。
  5. 管道上的数据节点按反向顺序返回确认信息,最终由管道中的第一个数据节点将整条管道的确认信息发送给客户端。
  6. 客户端完成写入,调用close()方法关闭文件输出流。
  7. 通知NameNode文件写入成功。

3.3.4 数据容错

  1. 数据节点(DataNode)出错

每个DataNode周期性地向NameNode发送心跳信号,网络割裂会导致DataNode与NameNode失去联系。NameNode通过心跳信号的缺失来检测这一情况,并将这些近期不再发送心跳信号的DataNode标志为宕机,不会再将新的I/O请求发送给它。DataNode的宕机可能引发一些数据块的副本数低于指定值,NameNode不断检测这些需要复制的数据块,一旦发现某数据块的副本数低于设定副本数就启动复制操纵。再某个DataNode节点失效,某个副本遭到损坏,DataNode上的硬盘出现错误,或者文件的副本系数增大时,数据块就需要重新复制。

  1. 名称节点(NameNode)出错

HDFS中所有的元数据都保存在NameNode上,名称节点维护着两个重要文件:fsimage和edits。如果这两个文件发生损坏,整个HDFS将失效。Hadoop采用两种机制来确保名称节点的安全。第一种,把名称节点上的元数据信息同步存储在其他文件系统(比如网络文件系统NFS)中,当名称节点失效时,可以到有元数据的文件系统中获取元数据信息。后面提到的HDFS HA就是采用共享存储系统来存储edits的。当一个NameNode出现故障时,HDFS自动切换到备用NameNode上,元信息则从共享存储系统中获取。第二种,运行一个SecondaryNameNode,当名称节点宕机后,可以把SecondaryNameNode作为一种补救措施,利用SecondaryNameNode中的元数据信息进行系统恢复,但是这种方法仍然会有部分数据丢失。通常情况下会把这两种方法结合使用。

  1. 数据出错

在HDFS架构使用过程中,可能会出现数据损坏的情况。这种情况发生时HDFS的处理步骤如下:

  1. DataNode读取Block时,会计算Checksum(校验和)。
  2. 如果计算出的Checksum(校验和)与Block创建时值不一样,说明该Block已经损坏。
  3. Client读取其他DataNode上的Block。
  4. NameNode标记该Block已经损坏,然后复制Block达到预先设置的文件副本数。
  5. DataNode在新文件创建三周后验证其Checksum。