HDFS是Hadoop Distribute File System 的简称,也就是Hadoop的一个分布式文件系统。
一、HDFS的主要设计理念
1、存储超大文件
这里的“超大文件”是指几百MB、GB甚至TB级别的文件。
2、最高效的访问模式是 一次写入、多次读取(流式数据访问)
HDFS存储的数据集作为hadoop的分析对象。在数据集生成后,长时间在此数据集上进行各种分析。每次分析都将设计该数据集的大部分数据甚至全部数据,因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。
3、运行在普通廉价的服务器上
HDFS设计理念之一就是让它能运行在普通的硬件之上,即便硬件出现故障,也可以通过容错策略来保证数据的高可用。
二、HDFS的忌讳
1、将HDFS用于对数据访问要求低延迟的场景
由于HDFS是为高数据吞吐量应用而设计的,必然以高延迟为代价。
2、存储大量小文件
HDFS中元数据(文件的基本信息)存储在namenode的内存中,而namenode为单点,小文件数量大到一定程度,namenode内存就吃不消了。
三、HDFS基本概念
数据块(block):大文件会被分割成多个block进行存储,block大小默认为64MB。每一个block会在多个datanode上存储多份副本,默认是3份。这样设计HDFS的块是为了最小化寻址开销,如果块设置的足够大,使得磁盘传输数据的时间明显大于定位这个块开始所需的时间,那么块寻址的时间即可忽略不计。但是数据块也不应该设置的过大,因为MapReduce中的map任务通常一次处理一个块中的数据,因此如果任务量太少(少于节点的数量),作业运行的速度就会变慢。
对分布式文件中块的抽象的好处有:
1.一个文件的大小可以大于网络中任意一个磁盘的容量。
2.使用块抽象而不是整个文件作为存储单元,大大简化了存储子系统的设计。
namenode:namenode负责管理文件目录、文件和block的对应关系以及block和datanode的对应关系。
datanode:datanode就负责存储了,当然大部分容错机制都是在datanode上实现的。
四、HDFS基本架构图
图中有几个概念需要介绍一下
Rack 是指机柜的意思,一个block的三个副本通常会保存到两个或者两个以上的机柜中(当然是机柜中的服务器),这样做的目的是做防灾容错,因为发生一个机柜掉电或者一个机柜的交换机挂了的概率还是蛮高的。
五.HDFS中的数据流
文件读取剖析
1.客户端通过调用FileSystem对象中的open()方法来打开希望读取的文件,对于HDFS来说,这个对象是分布式文件系统的一个实例。
2.DistributedFileSystem通过使用RPC来调用namenode,以确定文件起始块的位置。另外,DistributedFileSystem类返回一个FSDataInputStream对象用来给客户端读取数据,FSDInputStream类转而封装DFSInputStream对象,该对象管理着datanode和namenode的I/O。
3.客户端对这个输入流调用read()方法,存储着起始块的datanode地址的DFSInputStream随即连接距离最近的datanode。
4.通过对数据流反复调用read()方法,可以将数据从datanode传输到客户端。在读取数据的时候,如果DFSInputStream与datanode通信时遇到错误时,它便会尝试才能够这个块的另一个最近邻的datanode读取数据。
5.到达块的末端时,DFSInputStream会关闭与该datanode的连接,然后寻找下一个块的最佳datanode。客户端读取数据文件时,块是按照打开DFSInputStream与datanode连接的顺序读取的。它也需要询问namenode来检索下一批所需块的datanode的位置。
6.一旦客户端读取完成,就对FSDataInputStream调用close()方法。
这个过程中,namenode只需要响应块位置的请求,不需要响应数据请求,否则随着客户端数量的增长,namenode很快会成为一个瓶颈。
文件写入剖析
1.客户端通过对DistributedFileSystem对象调用create()函数来创建文件。
2.DistributedFileSystem对namenode创建一个RPC调用,在文件系统的命名空间中创建一个新文件,此时该文件还没有相应的数据块。另外,DistributedFileSystem类返回一个FSDataOutputStream对象,对此客户端可以开始写入数据。
3.像读取数据一样,FSDOutputStream类转而封装DFSOutputStream对象,该对象管理着datanode和namenode的I/O。此时客户端可以开始向FSDataOutputStream写入数据,该数据写入到一个内部队列,称为”数据队列“。
4.DataStreamer处理数据队列,它的责任是根据datanode列表来要求namenode分配合适的新块来存储数据备份。这一组datanode构成一个管线,假设副本为3,所以管线有3个节点,DataStreamer将数据包流式传输到管线中第一个datanode,该datanode存储数据包并将它发送到管线中的第2个datanode,然后第2个存储数据并发送给第3个datanode。
5.DFSOutputStream也维护着一个内部数据包队列来等待datanode的受到确认回执,称为“确认队列”。当收到管道中所有datanode确认信息后,该数据包才会确认队列删除。在数据写入期间,datanode如果发生故障,首先会关闭管线,确认把队列中的任何数据包都添加回数据队列的最前端,以确保故障节点下游的datanode不会漏掉任何一个数据包。
6.客户端完成对数据的写入后,会对数据流调用close()方法。
7.该操作将剩余的所有数据包写入datanode管线中,并在联系namenode且发送文件写入完成信号之前,等待确认。
副本的布局
namenode如何选择在哪个datanode存储副本??这需要在可靠性,写入宽带和读取宽带之间进行权衡。
hadoop的默认策略是在运行客户端的节点上放1个副本(如果client运行在集群外,就随即选择一个节点),第二个副本放在与第一个不同且随机的另外一个机架中的节点上,第三个副本放在与第二个副本同机架不同节点上。