本文主要从两个方面对hdfs进行阐述,第一就是hdfs的整个架构以及组成,第二就是hdfs文件的读写流程。html
1、HDFS概述node
标题中提到hdfs(Hadoop Distribute File System)是分布式文件系统linux
分布式文件系统 distributed file system 是指文件系统管理的物理存储资源不必定直接连接在本地节点上,而是经过计算机网络与节点相连,可以让多机器上的多用户分享文件和存储空间。分布式文件系统的设计基于客户机/服务器模式sql
分布式文件系统的特色:
一、分布式文件系统能够有效解决数据的存储和管理难题
二、将固定于某个地点的某个文件系统,扩展到任意多个地点/多个文件系统
三、众多的节点组成一个文件系统网络
四、每一个节点能够分布在不一样的地点,经过网络进行节点间的通讯和数据传输
五、在使用分布式文件系统时,无需关心数据是存储在哪一个节点上、或者是从哪一个节点获取的,只须要像使用本地文件系统同样管理和存储文件系统中的数据apache
Hadoop之(HDFS)是一种分布式文件系统,设计用于在商用硬件上运行。 它与现有的分布式文件系统有许多类似之处。 可是,与其余分布式文件系统的差别很大。
HDFS具备高度容错能力,旨在部署在低成本硬件上。
HDFS提供对应用程序数据的高吞吐量访问,适用于具备大型数据集的应用程序。
HDFS放宽了一些POSIX要求,以实现对文件系统数据的流式访问安全
HDFS优点:服务器
一、可构建在廉价机器上,设备成本相对低
二、高容错性,HDFS将数据自动保存多个副本,副本丢失后,自动恢复,防止数据丢失或损坏
三、适合批处理,HDFS适合一次写入、屡次查询(读取)的状况,适合在已有的数据进行屡次分析,稳定性好
四、适合存储大文件,其中的大表示能够存储单个大文件,由于是分块存储,以及表示存储大量的数据
HDFS劣势:网络
一、因为提升吞吐量,下降实时性
二、因为每一个文件都会在namenode中记录元数据,若是存储了大量的小文件,会对namenode形成很大的压力
三、不合适小文件处理,在mapreduce的过程当中小文件的数量会形成map数量的增大,致使资源被占用,并且速度慢。
四、不适合文件的修改,文件只能追加在文件的末尾,不支持任意位置修改,不支持多个写入者操做
2、HDFS架构架构
hdfs架构图以下图所示:并发
HDFS具备主/从架构。HDFS集群由单个NameNode,和多个datanode构成。
NameNode:管理文件系统命名空间的主服务器和管理客户端对文件的访问组成,如打开,关闭和重命名文件和目录。负责管理文件目录、文件和block的对应关系以及block和datanode的对应关系,维护目录树,接管用户的请求。以下图所示:
一、将文件的元数据保存在一个文件目录树中
二、在磁盘上保存为:fsimage 和 edits
三、保存datanode的数据信息的文件,在系统启动的时候读入内存。
DataNode:(数据节点)管理链接到它们运行的节点的存储,负责处理来自文件系统客户端的读写请求。DataNodes还执行块建立,删除
Client:(客户端)表明用户经过与nameNode和datanode交互来访问整个文件系统,HDFS对外开放文件命名空间并容许用户数据以文件形式存储。用户经过客户端(Client)与HDFS进行通信交互。
块和复制:
咱们都知道linux操做系统中的磁盘的块的大小默认是512,而hadoop2.x版本中的块的大小默认为128M,那为何hdfs中的存储块要设计这么大呢?
其目的是为了减少寻址的开销。只要块足够大,磁盘传输数据的时间一定会明显大于这个块的寻址时间。
那为何要以块的形式存储文件,而不是整个文件呢?
一、由于一个文件能够特别大,能够大于有个磁盘的容量,因此以块的形式存储,能够用来存储不管大小怎样的文件。
二、简化存储系统的设计。由于块是固定的大小,计算磁盘的存储能力就容易多了
三、以块的形式存储不须要所有存在一个磁盘上,能够分布在各个文件系统的磁盘上,有利于复制和容错,数据本地化计算
块和复本在hdfs架构中分布以下图所示:
既然namenode管理着文件系统的命名空间,维护着文件系统树以及整颗树内的全部文件和目录,这些信息以文件的形式永远的保存在本地磁盘上,分别问命名空间镜像文件fsimage和编辑日志文件Edits。datanode是文件的工做节点,根据须要存储和检索数据块,而且按期的向namenode发送它们所存储的块的列表。那么就知道namenode是多么的重要,一旦那么namenode挂了,那整个分布式文件系统就不可使用了,因此对于namenode的容错就显得尤其重要了,hadoop为此提供了两种容错机制:
容错机制一:
就是经过对那些组成文件系统的元数据持久化,分别问命名空间镜像文件fsimage(文件系统的目录树)和编辑日志文件Edits(针对文件系统作的修改操做记录)。磁盘上的映像FsImage就是一个Checkpoint,一个里程碑式的基准点、同步点,有了一个Checkpoint以后,NameNode在至关长的时间内只是对内存中的目录映像操做,同时也对磁盘上的Edits操做,直到关机。下次开机的时候,NameNode要从磁盘上装载目录映像FSImage,那其实就是老的Checkpoint,也许就是上次开机后所保存的映像,而自从上次开机后直到关机为止对于文件系统的全部改变都记录在Edits文件中;将记录在Edits中的操做重演于上一次的映像,就获得这一次的新的映像,将其写回磁盘就是新的Checkpoint(也就是fsImage)。可是这样有很大一个缺点,若是Edits很大呢,开机后生成原始映像的过程也会很长,因此对其进行改进:每当 Edits长到必定程度,或者每隔必定的时间,就作一次Checkpoint,可是这样就会给namenode形成很大的负荷,会影响系统的性能。因而就有了SecondaryNameNode的须要,这至关于NameNode的助理,专替NameNode作Checkpoint。固然,SecondaryNameNode的负载相比之下是偏轻的。因此若是为NameNode配上了热备份,就可让热备份兼职,而无须再有专职的SecondaryNameNode。因此架构图以下图所示:
SecondaryNameNode工做原理图:
SecondaryNameNode主要负责下载NameNode中的fsImage文件和Edits文件,并合并生成新的fsImage文件,并推送给NameNode,工做原理以下:
一、secondarynamenode请求主namenode中止使用edits文件,暂时将新的写操做记录到一个新的文件中;
二、secondarynamenode从主namenode获取fsimage和edits文件(经过http get)
三、secondarynamenode将fsimage文件载入内存,逐一执行edits文件中的操做,建立新的fsimage文件。
四、secondarynamenode将新的fsimage文件发送回主namenode(使用http post).
五、namenode用从secondarynamenode接收的fsimage文件替换旧的fsimage文件;用步骤1所产生的edits文件替换旧的edits文件。同时,还更新fstime文件来记录检查点执行时间。
六、最终,主namenode拥有最新的fsimage文件和一个更小的edits文件。当namenode处在安全模式时,管理员也可调用hadoop dfsadmin –saveNameSpace命令来建立检查点。
从上面的过程当中咱们清晰的看到secondarynamenode和主namenode拥有相近内存需求的缘由(由于secondarynamenode也把fsimage文件载入内存)。所以,在大型集群中,secondarynamenode须要运行在一台专用机器上。
建立检查点的触发条件受两个配置参数控制。一般状况下,secondarynamenode每隔一小时(有fs.checkpoint.period属性设置)建立检查点;此外,当编辑日志的大小达到64MB(有fs.checkpoint.size属性设置)时,也会建立检查点。系统每隔五分钟检查一次编辑日志的大小。
容错机制二:
高可用方案(详情见:hadoop高可用安装和原理详解)
3、HDFS读数据流程
HDFS读数据流程以下图所示:
一、客户端经过FileSystem对象(DistributedFileSystem)的open()方法来打开但愿读取的文件。
二、DistributedFileSystem经过远程调用(RPC)来调用namenode,获取到每一个文件的起止位置。对于每个块,namenode返回该块副本的datanode。这些datanode会根据它们与客户端的距离(集群的网络拓扑结构)排序,若是客户端自己就是其中的一个datanode,那么就会在该datanode上读取数据。DistributedFileSystem远程调用后返回一个FSDataInputStream(支持文件定位的输入流)对象给客户端以便于读取数据,而后FSDataInputStream封装一个DFSInputStream对象。该对象管理datanode和namenode的IO。
三、客户端对这个输入流调用read()方法,存储着文件起始几个块的datanode地址的DFSInputStream随即链接距离最近的文件中第一个块所在的datanode,经过数据流反复调用read()方法,能够将数据从datanode传送到客户端。当读完这个块时,DFSInputStream关闭与该datanode的链接,而后寻址下一个位置最佳的datanode。
客户端从流中读取数据时,块是按照打开DFSInputStream与datanode新建链接的顺序读取的。它也须要询问namenode来检索下一批所需块的datanode的位置。一旦客户端完成读取,就对FSDataInputStream调用close()方法。
注意:在读取数据的时候,若是DFSInputStream在与datanode通信时遇到错误,它便会尝试从这个块的另一个临近datanode读取数据。他也会记住那个故障datanode,以保证之后不会反复读取该节点上后续的块。DFSInputStream也会经过校验和确认从datanode发送来的数据是否完整。若是发现一个损坏的块, DFSInputStream就会在试图从其余datanode读取一个块的复本以前通知namenode。
总结:在这个设计中,namenode会告知客户端每一个块中最佳的datanode,并让客户端直接联系该datanode且检索数据。因为数据流分散在该集群中的全部datanode,因此这种设计会使HDFS可扩展到大量的并发客户端。同时,namenode仅须要响应位置的请求(这些信息存储在内存中,很是高效),而无需响应数据请求,不然随着客户端数量的增加,namenode很快会成为一个瓶颈。
4、HDFS写数据流程
HDFS写数据流程图以下图所示:
一、首先客户端经过DistributedFileSystem上的create()方法指明一个预建立的文件的文件名
二、DistributedFileSystem再经过RPC调用向NameNode申请建立一个新文件(这时该文件尚未分配相应的block)。namenode检查是否有同名文件存在以及用户是否有相应的建立权限,若是检查经过,namenode会为该文件建立一个新的记录,不然的话文件建立失败,客户端获得一个IOException异常。DistributedFileSystem返回一个FSDataOutputStream以供客户端写入数据,与FSDataInputStream相似,FSDataOutputStream封装了一个DFSOutputStream用于处理namenode与datanode之间的通讯。
三、当客户端开始写数据时(,DFSOutputStream把写入的数据分红包(packet), 放入一个中间队列——数据队列(data queue)中去。DataStreamer从数据队列中取数据,同时向namenode申请一个新的block来存放它已经取得的数据。namenode选择一系列合适的datanode(个数由文件的replica数决定)构成一个管道线(pipeline),这里咱们假设replica为3,因此管道线中就有三个datanode。
四、DataSteamer把数据流式的写入到管道线中的第一个datanode中,第一个datanode再把接收到的数据转到第二个datanode中,以此类推。
五、DFSOutputStream同时也维护着另外一个中间队列——确认队列(ack queue),确认队列中的包只有在获得管道线中全部的datanode的确认之后才会被移出确认队列
若是某个datanode在写数据的时候当掉了,下面这些对用户透明的步骤会被执行:
管道线关闭,全部确认队列上的数据会被挪到数据队列的首部从新发送,这样能够确保管道线中当掉的datanode下流的datanode不会由于当掉的datanode而丢失数据包。
在还在正常运行的datanode上的当前block上作一个标志,这样当当掉的datanode从新启动之后namenode就会知道该datanode上哪一个block是刚才当机时残留下的局部损坏block,从而能够把它删掉。
已经当掉的datanode从管道线中被移除,未写完的block的其余数据继续被写入到其余两个还在正常运行的datanode中去,namenode知道这个block还处在under-replicated状态(也即备份数不足的状态)下,而后他会安排一个新的replica从而达到要求的备份数,后续的block写入方法同前面正常时候同样。有可能管道线中的多个datanode当掉(虽然不太常常发生),但只要dfs.replication.min(默认为1)个replica被建立,咱们就认为该建立成功了。剩余的replica会在之后异步建立以达到指定的replica数。
六、当客户端完成写数据后,它会调用close()方法。这个操做会冲洗(flush)全部剩下的package到pipeline中。
七、等待这些package确认成功,而后通知namenode写入文件成功。这时候namenode就知道该文件由哪些block组成(由于DataStreamer向namenode请求分配新block,namenode固然会知道它分配过哪些blcok给给定文件),它会等待最少的replica数被建立,而后成功返回。
注意:hdfs在写入的过程当中,有一点与hdfs读取的时候很是类似,就是:DataStreamer在写入数据的时候,每写完一个datanode的数据块,都会从新向nameNode申请合适的datanode列表。这是为了保证系统中datanode数据存储的均衡性。
hdfs写入过程当中,datanode管线的确认应答包并非每写完一个datanode,就返回一个确认应答,而是一直写入,直到最后一个datanode写入完毕后,统一返回应答包。若是中间的一个datanode出现故障,那么返回的应答就是前面无缺的datanode确认应答,和故障datanode的故障异常。这样咱们也就能够理解,在写入数据的过程当中,为何数据包的校验是在最后一个datanode完成。
更多hadoop生态文章见: hadoop生态系列
参考:
《Hadoop权威指南 大数据的存储与分析 第四版》
https://hadoop.apache.org/docs/r2.7.7/hadoop-project-dist/hadoop-hdfs/HdfsUserGuide.html