HDFS-(Hadoop Distributed File System)架构与设计

737 阅读13分钟

image.png

引言

HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。HDFS放宽了一部分POSIX约束,来实现流式读取文件系统数据的目的。HDFS在最开始是作为Apache Nutch搜索引擎项目的基础架构而开发的。HDFS是Apache Hadoop Core项目的一部分。这个项目的地址是hadoop.apache.org/core/

前提和设计目标

硬件错误

硬件错误是常态而不是异常。HDFS可能由成百上千的服务器所构成,每个服务器上存储着文件系统的部分数据。我们面对的现实是构成系统的组件数目是巨大的,而且任一组件都有可能失效,这意味着总是有一部分HDFS的组件是不工作的。因此错误检测和快速、自动的恢复是HDFS最核心的架构目标。

流式数据访问

运行在HDFS上的应用和普通的应用不同,需要流式访问它们的数据集。HDFS的设计中更多的考虑到了数据批处理,而不是用户交互处理。比之数据访问的低延迟问题,更关键的在于数据访问的高吞吐量。POSIX标准设置的很多硬性约束对HDFS应用系统不是必需的。为了提高数据的吞吐量,在一些关键方面对POSIX的语义做了一些修改。

大规模数据集

运行在HDFS上的应用具有很大的数据集。HDFS上的一个典型文件大小一般都在G字节至T字节。因此,HDFS被调节以支持大文件存储。它应该能提供整体上高的数据传输带宽,能在一个集群里扩展到数百个节点。一个单一的HDFS实例应该能支撑数以千万计的文件。

简单的一致性模型

HDFS应用需要一个“一次写入多次读取”的文件访问模型。一个文件经过创建、写入和关闭之后就不需要改变。这一假设简化了数据一致性问题,并且使高吞吐量的数据访问成为可能。Map/Reduce应用或者网络爬虫应用都非常适合这个模型。目前还有计划在将来扩充这个模型,使之支持文件的附加写操作。

“移动计算比移动数据更划算”

一个应用请求的计算,离它操作的数据越近就越高效,在数据达到海量级别的时候更是如此。因为这样就能降低网络阻塞的影响,提高系统数据的吞吐量。将计算移动到数据附近,比之将数据移动到应用所在显然更好。HDFS为应用提供了将它们自己移动到数据附近的接口。

异构软硬件平台间的可移植性

HDFS在设计的时候就考虑到平台的可移植性。这种特性方便了HDFS作为大规模数据应用平台的推广。

不适用场景

不适合低延时数据访问

1)它适合高吞吐率的场景,就是在某一时间内写入大量的数据。但是它在低延时的情况下是不行的,比如毫秒级以内读取数据。

无法高效的对大量小文件进行存储

1) 存储大量小文件的话,它会占用  NameNode大量的内存来存储文件、目录和块信息。这样是不可取的,因为NameNode的内存总是有限的。

2) 小文件存储的寻道时间会超过读取时间,它违反了HDFS的设计目标。

并发写入、文件随机修改

1) 一个文件只能有一个写,不允许多个线程同时写。

2) 仅支持数据 append(追加),不支持文件的随机修改。

HDFS的一些关键元素

image.png

image.png

Block

HDFS被设计成支持大文件,适用HDFS的是那些需要处理大规模的数据集的应用。这些应用都是只写入数据一次,但却读取一次或多次,并且读取速度应能满足流式读取的需要。HDFS支持文件的“一次写入多次读取”语义。一个典型的数据块大小是64MB。因而,HDFS中的文件总是按照64M被切分成不同的块,每个块尽可能地存储于不同的Datanode中。

NameNode

HDFS支持传统的层次型文件组织结构。用户或者应用程序可以创建目录,然后将文件保存在这些目录里。文件系统名字空间的层次结构和大多数现有的文件系统类似:用户可以创建、删除、移动或重命名文件。当前,HDFS不支持用户磁盘配额和访问权限控制,也不支持硬链接和软链接。但是HDFS架构并不妨碍实现这些特性。

Namenode负责维护文件系统的名字空间,任何对文件系统名字空间或属性的修改都将被Namenode记录下来。应用程序可以设置HDFS保存的文件的副本数目。文件副本的数目称为文件的副本系数,这个信息也是由Namenode保存的。

DataNode

Namenode全权管理数据块的复制,它周期性地从集群中的每个Datanode接收心跳信号和块状态报告(Blockreport)。接收到心跳信号意味着该Datanode节点工作正常。块状态报告包含了一个该Datanode上所有数据块的列表。

HDFS Datanodes

NameNodeDataNode
存储元数据存储文件内容
元数据保存在内存中文件内容保存在磁盘中
保存文件-block-DataNode之间的映射关系维护了block id 到datanode 本地文件的映射关系

SecondaryNameNode

定期合并 NameNode 的 edit logs(对文件系统的改动序列) 到 fsimage(对整个文件系统的快照),并拷贝修改后的 fsimage 到 NameNode。

提供一个 NameNode 的检查点(切忌认为是 NameNode 的备份),可用于 NameNode 的故障恢复。

HDFS出错与恢复

HDFS具有较高的容错性,可以兼容廉价的硬件,它把硬件出错看作一种常态,而不是异常,并设计了相应的机制检测数据错误和进行自动恢复,主要包括以下几种情形:名称节点出错、数据节点出错和数据出错。

  名称节点出错

名称节点保存了所有的元数据信息,其中,最核心的两大数据结构是FsImage和Editlog,如果这两个文件发生损坏,那么整个HDFS实例将失效。因此,HDFS设置了备份机制,把这些核心文件同步复制到备份服务器SecondaryNameNode上。当名称节点出错时,就可以根据备份服务器SecondaryNameNode中的FsImage和Editlog数据进行恢复。

  数据节点出错

每个数据节点会定期向名称节点发送“心跳”信息,向名称节点报告自己的状态。

当数据节点发生故障,或者网络发生断网时,名称节点就无法收到来自一些数据节点的心跳信息,这时,这些数据节点就会被标记为“宕机”,节点上面的所有数据都会被标记为“不可读”,名称节点不会再给它们发送任何I/O请求。

这时,有可能出现一种情形,即由于一些数据节点的不可用,会导致一些数据块的副本数量小于冗余因子。

名称节点会定期检查这种情况,一旦发现某个数据块的副本数量小于冗余因子,就会启动数据冗余复制,为它生成新的副本。

HDFS和其它分布式文件系统的最大区别就是可以调整冗余数据的位置。

数据出错

网络传输和磁盘错误等因素,都会造成数据错误。

客户端在读取到数据后,会采用md5和sha1对数据块进行校验,以确定读取到正确的数据。

在文件被创建时,客户端就会对每一个文件块进行信息摘录,并把这些信息写入到同一个路径的隐藏文件里面。

当客户端读取文件的时候,会先读取该信息文件,然后,利用该信息文件对每个读取的数据块进行校验,如果校验出错,客户端就会请求到另外一个数据节点读取该文件块,并且向名称节点报告这个文件块有错误,名称节点会定期检查并且重新复制这个块。

数据读取过程中出错

  1. 读取过程中 DataNode 挂了
  2. 读取到的文件数据损坏

HDFS 的文件块多副本分散存储机制保障了数据存储的可靠性,对于第一种情况 DataNode 挂了只需要失败转移到其他副本所在的 DataNode 继续读取。

而对于第二种情况读取到的文件数据块若校验失败可认定为损坏,依然可以转移到读取其他完好的副本,并向 NameNode 汇报该文件 block 损坏,后续处理由 NameNode 通知 DataNode 删除损坏文件 block,并根据完好的副本来复制一份新的文件 block 副本。

数据写入过程中出错

可能的异常模式如下所列:

  1. Client 在写入过程中,自己挂了
  2. Client 在写入过程中,有 DataNode 挂了
  3. Client 在写入过程中,NameNode 挂了

对于以上所列的异常模式,都有分别对应的恢复模式。

当 Client 在写入过程中,自己挂了。由于 Client 在写文件之前需要向 NameNode 申请该文件的租约(lease),只有持有租约才允许写入,而且租约需要定期续约。所以当 Client 挂了后租约会超时,HDFS 在超时后会释放该文件的租约并关闭该文件,避免文件一直被这个挂掉的 Client 独占导致其他人不能写入。这个过程称为 lease recovery。在发起 lease recovery 时,若多个文件 block 副本在多个 DataNodes 上处于不一致的状态,首先需要将其恢复到一致长度的状态。这个过程称为 block recovery。 这个过程只能在 lease recovery 过程中发起。

当 Client 在写入过程中,有 DataNode 挂了。写入过程不会立刻终止(如果立刻终止,易用性和可用性都太不友好),取而代之 HDFS 尝试从流水线中摘除挂了的 DataNode 并恢复写入,这个过程称为 pipeline recovery。这样会导致副本数量不满足要求,之后Namenode检查时会发出请求,复制一份Block.

当 Client 在写入过程中,NameNode 挂了。这里的前提是已经开始写入了,所以 NameNode 已经完成了对 DataNode 的分配,若一开始 NameNode 就挂了,整个 HDFS 是不可用的所以也无法开始写入。流水线写入过程中,当一个 block 写完后需向 NameNode 报告其状态,这时 NameNode 挂了,状态报告失败,但不影响 DataNode 的流线工作,数据先被保存下来,但最后一步 Client 写完向 NameNode 请求关闭文件时会出错,由于 NameNode 的单点特性,所以无法自动恢复,需人工介入恢复。

HDFS数据读写过程

文件读取过程

image.png

具体步骤:

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

文件写入过程

image.png

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

hdfs dfs 使用命令

常用:

创建目录dir1和dir2:\
hdfs dfs -mkdir -p /project/test/dir1 /project/test/dir2

显示文件夹test下的目录:\
hdfs dfs -ls /project/test/

将本地文件上传至hdfs目标路径:\
hdfs dfs -put /home/bb/test1.txt /project/test/

修改文件的权限,1:可读、2:可写、4:可执行
hdfs dfs -chmod 文件路径 755


查询前几行样本数据:\
hdfs dfs -cat /project/test/test.txt | head -17

查询后5行数据:\
hdfs dfs -cat /project/test/test.txt | tail -5

随机返回指定行数的样本:\
hdfs dfs -cat /project/test/test.txt | shuf -n 5

查看文本的行数:\
hdfs dfs -cat /project/test/test.txt | wc -l

查询第五行之后的文件内容:\
hdfs dfs -cat /project/test/test.txt | tail -n +5

查询过滤字段num:\
hdfs dfs -cat /project/test/test.txt | grep num

查看文件大小:\
hdfs dfs -du /project/test/test.txt\
hdfs dfs -du /project/test/*

显示文件夹下面文件的数量:\
hdfs dfs -count /project/test/\
递归的显示test下的目录:\
hdfs dfs -ls -R /project/test/或hdfs dfs -lsr /project/test/

将文件从目标路径拷贝到本地:\
hdfs dfs -get /project/test/test1 /home/bb/

将文件或目录复制到目标路径:\
hdfs dfs -cp 源路径 目标路径

删除文件或目录:\
hdfs dfs -rm 路径\
删除文件夹及文件夹下的内容:\
hdfs dfs -rm -r 路径\
跳过垃圾桶删除:\
hdfs dfs -rm -r -skipTrash 路径

以文本的方式显示到标准输出:\
hdfs dfs -text /project/test/

显示文件的最后1KB到标准输出:\
hdfs dfs -tail -f /project/test/t.txt

image.png