引言
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的一些关键元素
Block
HDFS被设计成支持大文件,适用HDFS的是那些需要处理大规模的数据集的应用。这些应用都是只写入数据一次,但却读取一次或多次,并且读取速度应能满足流式读取的需要。HDFS支持文件的“一次写入多次读取”语义。一个典型的数据块大小是64MB。因而,HDFS中的文件总是按照64M被切分成不同的块,每个块尽可能地存储于不同的Datanode中。
NameNode
HDFS支持传统的层次型文件组织结构。用户或者应用程序可以创建目录,然后将文件保存在这些目录里。文件系统名字空间的层次结构和大多数现有的文件系统类似:用户可以创建、删除、移动或重命名文件。当前,HDFS不支持用户磁盘配额和访问权限控制,也不支持硬链接和软链接。但是HDFS架构并不妨碍实现这些特性。
Namenode负责维护文件系统的名字空间,任何对文件系统名字空间或属性的修改都将被Namenode记录下来。应用程序可以设置HDFS保存的文件的副本数目。文件副本的数目称为文件的副本系数,这个信息也是由Namenode保存的。
DataNode
Namenode全权管理数据块的复制,它周期性地从集群中的每个Datanode接收心跳信号和块状态报告(Blockreport)。接收到心跳信号意味着该Datanode节点工作正常。块状态报告包含了一个该Datanode上所有数据块的列表。
NameNode | DataNode |
---|---|
存储元数据 | 存储文件内容 |
元数据保存在内存中 | 文件内容保存在磁盘中 |
保存文件-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对数据块进行校验,以确定读取到正确的数据。
在文件被创建时,客户端就会对每一个文件块进行信息摘录,并把这些信息写入到同一个路径的隐藏文件里面。
当客户端读取文件的时候,会先读取该信息文件,然后,利用该信息文件对每个读取的数据块进行校验,如果校验出错,客户端就会请求到另外一个数据节点读取该文件块,并且向名称节点报告这个文件块有错误,名称节点会定期检查并且重新复制这个块。
数据读取过程中出错
- 读取过程中 DataNode 挂了
- 读取到的文件数据损坏
HDFS 的文件块多副本分散存储机制保障了数据存储的可靠性,对于第一种情况 DataNode 挂了只需要失败转移到其他副本所在的 DataNode 继续读取。
而对于第二种情况读取到的文件数据块若校验失败可认定为损坏,依然可以转移到读取其他完好的副本,并向 NameNode 汇报该文件 block 损坏,后续处理由 NameNode 通知 DataNode 删除损坏文件 block,并根据完好的副本来复制一份新的文件 block 副本。
数据写入过程中出错
可能的异常模式如下所列:
- Client 在写入过程中,自己挂了
- Client 在写入过程中,有 DataNode 挂了
- 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数据读写过程
文件读取过程
具体步骤:
- HDFS客户端通过DistributeFileSystem对象的open()方法打开需要读取的文件
- DistributeFileSystem向远程的NameNode节点发起RPC调用,得到文件的数据块信息,返回数据块列表。(对于每个数据块,NameNode返回的是该数据块的DataNode地址)
- DistributeFileSystem返回一个FSDataInputStream对象给客户端, 客户端调用FSDataInputStream对象的read()方法读取数据
- 通过对数据流返回调用read() 方法,把数据从数据节点传输到客户端
- 当一个节点的数据读取完毕后,FSDataInputStream对象会关闭与此DataNode连接,然后连接此文件下一个数据块的最近数据节点
- 当客户端读取完数据时,调用FSDataInputStream对象的close() 方法关闭输入流
文件写入过程
- 客户端调用DistributedFileSystem 对象的create() 方法创建一个文件输出流对象
- DistributedFileSystem对象向远程的NameNode节点发起一次RPC调用,NameNode检查这个文件是否存在以及客户端是否有权限新建文件
- 客户端调用FSDataOutputStream对象的write() 方法写数据(数据先被写入缓冲区,再被钱分成一个个数据包)
- 每个数据包被发送到由NameNode节点分配的一组数据节点中的一个数据节点上,在这组数据节点组成的管道上一次传输数据包
- 管道上的节点按反向顺序返回确认信息,最终由管道的第一个数据节点将整条管道的确认信息发送给客户端
- 客户端完成写入,调用close()方法关闭文件输出流
- 通知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