HDFS分布式文件系统,即Hadoop Distributed Filesystem,是一个分布式文件系统,被设计部署在廉价硬件上。HDFS是一个高容错,被设计部署在廉价硬件上。HDFS提供高吞吐量访问数据,而且适用于大数据集应用程序。HDFS提供POSIX(可移植性操做系统接口)标准流式数据访问。html
硬件故障做为常态处理,而不是做为异常。整个HDFS系统可能包含成百上千的服务器,每一个服务器都存储了文件系统的一部分。系统包含如此多的组成部分,每一个组成部分都会频繁地出现故障,这意味着HDFS的一些组成部分老是失效的。快速的故障检测和自动修复是HDFS的核心架构目标。java
运行在HDFS上的应用程序必须流式的访问数据集。他们并非运行在常规文件系统上的常规程序。HDFS的设计更适合进行批处理做业,而不是用户交互式的;重点在于数据访问的高吞吐量而不是数据访问的低延迟。POSIX强制的不少硬性需求对不少应用来讲不是必需的,去掉POSIX的不少关键地方的语义,能够得到更好的数据吞吐量。node
运行在HDFS上的应用程序都是大型数据集。这意味着典型的HDFS文件大小是从GB到TB级别。所以,HDFS在单个集群中就能提供高聚合数据带宽和数百个节点的规模,能支持一个集群中千万级的文件数。apache
大部分的HDFS程序对于文件操做都是一次写入屡次读取。文件一旦建立、写入、关闭以后就不须要进行修改了。这个假定简化了数据一致性问题,而且支持高吞吐量的访问。Mapreduce和网络爬虫程序都是很是完美地符合这个模型。bootstrap
一个程序请求的计算,若是能在更接近数据存储的位置进行,那么将会更有效率。尤为是在大数据量状况下,这样能够最小化网络拥塞和提升整个系统的吞吐量。这个假定就是将计算离数据更近比将文件移动到程序运行的位置更好。HDFS提供了接口,让程序将本身移动到离数据存储更近的位置运行。api
HDFS被设计成能够方便的从一个平台移植到另一个平台,这样有助于HDFS被更多的大数据集应用程序采用。浏览器
HDFS是一种Master/Slave架构。一个HDFS集群有一个独立的Namenode,这个Master用来管理文件系统的命名空间和客户端的文件访问。另外,集群中还有一些Datanodes,通常来讲每一个Datanode管理本节点上的存储(磁盘)。HDFS暴露一个文件文件系统命名空间,而且容许用户数据存储成文件。在HDFS内部,一个文件将被切分红一个或者多个数据块,而这些数据块被存储在一组Datanodes中。Namenode执行名字空间的文件和目录的操做,好比打开、关闭和重命名;它同时决定了数据块在数据节点上的映射分布。Datanodes负责为文件系统客户端的读写请求提供服务。根据Namenode的指示,Datanodes同时扮演了建立、删除和复制数据块的角色。缓存
一言以蔽之,Namenode存储名字空间。Datanodes存储文件系统数据块。Namenode控制文件系统的访问,Datanode响应Namenode的指示来操做数据块。安全
Namenode和Datanode分别运行在普通的机器上。典型的就是GNU/Linux操做系统。使用JAVA语言编写,任何支持JAVA的机器(理论上)都能运行Namenode和Datanode。高度移植语言JAVA的好处在于HDFS能够部署在大范围的机器上。典型的部署是,一台单独的机器部署Namenode,其余机器部署Datanode。服务器
集群内部单个Namenode的存在极大地简化了系统体系。Namenode做为仲裁员,而且是全部HDFS元数据的存储仓库。系统这样设计可使得用户实际数据没必要通过Namenode。
The File System Namespace即文件系统命名空间。
HDFS支持传统继承式的文件组织结构。用户或者程序能够建立目录而且把文件存储在这些目录中。HDFS的名字空间层次结构跟大多数文件系统很是类似:建立、删除、从一个目录挪动文件到另外一个目录以及重命名。
Namenode维护文件系统命名空间。任何文件系统名字空间或者属性的改变都会被Namenode记录。应用程序能够指定文件在HDFS中的副本数。文件的副本数因子都是由Namenode进行存储。
HDFS经过块序列的形式存储每个文件,除了最后一个块的其余块都保持相同的大小。块的大小和副本数能够为每一个文件单独配置,副本数能够在建立以后进行修改。HDSF中的文件都是严格地要求任什么时候候只有一个写操做。Namenode支配Datanode进行数据块的复制,而且周期性地接受Datanode发送的心跳和块报告。一个块报告包括该数据节点的全部块列表。
副本位置的选择严重影响HDFS的可靠性和性能。
HDFS的副本分布采用了机架感知(Hadoop Rack Awareness)分布策略,这就保证了集群数据的可靠性,可用性和网络带宽利用率。大型的HDFS实例运行的服务器集群,一般分布在不一样的机架上。逻辑机架每一个对应一个交换机,不一样机架之间的通信也是经过汇聚网络交换机实现的。大多数状况下,同一个机架内机器之间的网络带宽要比不一样机架机器的网络带宽要高。
Namenode经过机架感知的过程决定了每一个Datanode所属的Rack id.一个简单而无优化的策略就是将副本分配到不一样的rack上。这种分布策略能够避免当一整个机架故障时出现的数据丢失,也能在读数据时最大化的利用不一样机架的带宽。可是这种策略增长了写成本,由于一个写须要将数据块副本发送到不一样的机架上。
一般状况下,副本因子设置为3。HDFS的分布策略是将第一个块写入一个机架服务器上,将第二个块写入同机架的另外一个机器,将第三个块写入另外一个机架的服务器上。这种策略将机架内部通信切分出来单独处理一般可以提高写性能。机架故障的概率要远远小于机器故障的概率。这种策略不会影响数据可靠性和可用性;然而它却减小了数据读取时消耗的汇聚网络带宽,由于一个块只分布在两个单独的机架上而不是三个。这种策略在没有影响数据可靠性和读取性能的前提下,提高了写入性能。
逻辑机架策略的设置能够经过脚本进行单独设置,这一部分咱们在集群管理的章节再作进一步的详细描述。
http://hadoop.apache.org/docs/current/hadoop-project-dist//hadoop-common/RackAwareness.html
为了减小全局网络带宽消耗和读取延迟,HDFS尽可能知足读取请求在距离最近的副本位置进行。读取请求发生时,本机架的服务器上副本要优先于其余机架上的副本。
HDFS启动时,Namenode会进入称为安全模式的特殊状态。当Namenode处于安全模式时,不会发生数据块的复制。Namenode接受数据节点的心跳信息和数据块报告。每一个数据块都设置了单独的副本数。
dfs.replication:默认块副本数。实际副本数能够在文件建立时指定,若是没有指定将使用默认值。
dfs.namenode.replication.min:数据块最小副本数。
dfs.replication.max:数据块最大副本数。
dfs.safemode.threshold.pct:指定应有多少比例的数据块知足最小副本数要求。小于等于0意味不进入安全模式,大于1意味一直处于安全模式。
副本数按dfs.replication设置,若是有失效节点致使某数据块副本数下降,当低于dfs.namenode.replication.min后,系统再在其余节点处复制新的副本。若是该数据块的副本常常丢失,致使在环境中太多的节点处复制了超dfs.replication.max的副本数,那么就再也不复制了。
hadoop的安全模式即只读模式,是指当前系统中数据块的副本数比较少,在该阶段要对数据块进行复制操做,不允外界对数据块进行修改和删除等操做。 可是当最小副本数设置为1时,安全模式意味着数据块不完整,也不能发生复制操做。NameNode在启动的时候首先进入安全模式,若是datanode丢失的block达到必定的比例(dfs.safemode.threshold.pct),则系统会一直处于安全模式状态即只读状态。 dfs.safemode.threshold.pct(缺省值0.999f)表示HDFS启动的时候,若是DataNode上报的block个数达到了元数据记录的block个数的0.999倍才能够离开安全模式,不然一直是这种只读模式。若是设为1则HDFS永远是处于SafeMode。
HDFS的名字空间是在Namenode上存储的。Namenode使用称之为EditsLog的事务日志来持久化每次发生在文件系统的修改。完整的文件系统名字空间、文件块的映射和文件系统的配置都存在一个叫FsImage的文件中,FsImage也是在Namenode的本地文件系统中储存。
Namenode在内存中有一个完整的文件系统名字空间,和文件块的映射镜像。这个元数据设计紧凑,4GB内存的Namenode也能轻松处理很是大的文件数和目录。当名字空间启动时,它将从磁盘中读取FsImage和EditsLog文件,而后将新的元数据刷新到本地磁盘中,生成一个新的FsImage文件,至此EditsLog文件已经被处理并持久化到FsImage中,这个过程叫作检查点。检查点一般发生到Namenode启动的时候;而且会周期性地进行检查点操做,默认3600;当事务数默认达到1000000的时候也会处罚检查点操做。
Datanode将HDFS数据以文件的形式存储在本地的文件系统中,它并不知道有关HDFS文件的信息。它把每一个HDFS数据块存储在本地文件系统的一个单独的文件中。Datanode并不在同一个目录建立全部的文件,实际上,它用试探的方法来肯定每一个目录的最佳文件数目,而且在适当的时候建立子目录。在同一个目录中建立全部的本地文件并非最优的选择,这是由于本地文件系统可能没法高效地在单个目录中支持大量的文件。当一个Datanode启动时,它会扫描本地文件系统,产生一个这些本地文件对应的全部HDFS数据块的列表,而后做为报告发送到Namenode,这个报告就是块状态报告。
全部的HDFS通信协议都是创建在TCP/IP协议之上。客户端经过一个可配置的TCP端口链接到Namenode,经过ClientProtocol协议与Namenode交互。而Datanode使用DatanodeProtocol协议与Namenode交互。一个远程过程调用(RPC)模型被抽象出来封装ClientProtocol和Datanodeprotocol协议。
在设计上,Namenode不会主动发起RPC,而是响应来自客户端或 Datanode 的RPC请求。
HDFS的主要目标就是即便在出错的状况下也要保证数据存储的可靠性。常见的三种出错状况是:Namenode出错, Datanode出错和网络割裂(network partitions)。
每一个Datanode节点周期性地向Namenode发送心跳信号。网络割裂可能致使一部分Datanode跟Namenode失去联系。Namenode经过心跳信号的缺失来检测这一状况,并将这些近期再也不发送心跳信号Datanode标记为宕机,不会再将新的IO请求发给它们。任何存储在宕机Datanode上的数据将再也不有效。Datanode的宕机可能会引发一些数据块的副本系数低于指定值,Namenode不断地检测这些须要复制的数据块,一旦发现就启动复制操做。
在下列状况下,可能须要从新复制:某个Datanode节点失效,某个副本遭到损坏,Datanode上的硬盘错误,或者文件的副本系数增大。
HDFS的架构支持数据均衡策略。当数据节点之间的空间占用比例超过一个设定的阈值时,能够手动启动balancer程序,就会自动地将数据从空间占用比例较大的Datanode移动到其余较空闲的Datanode。
在hadoop2.0中,datanode数据副本存放磁盘选择策略有两种方式:第一种是沿用hadoop1.0的磁盘目录轮询方式,实现类:RoundRobinVolumeChoosingPolicy.java;第二种是选择可用空间足够多的磁盘方式存储,实现类:AvailableSpaceVolumeChoosingPolicy.java。
若是不配置,默认使用第一种方式,既轮询选择磁盘来存储数据副本,可是轮询的方式虽然可以保证全部磁盘都可以被使用,可是常常会出现各个磁盘直接数据存储不均衡问题,有的磁盘存储得很满了,而有的磁盘可能还有不少存储空间没有获得利用,全部在hadoop2.0集群中,最好将磁盘选择策略配置成第二种,根据磁盘空间剩余量来选择磁盘存储数据副本,这样同样能保证全部磁盘都能获得利用,还能保证全部磁盘都被利用均衡。当配置AvailableSpaceVolumeChoosingPolicy时,还有额外的两个相关参数:dfs.datanode.available-space-volume-choosing-policy.balanced-space-threshold;
dfs.datanode.available-space-volume-choosing-policy.balanced-space-preference-fraction。
从某个Datanode获取的数据块有多是损坏的,损坏多是由Datanode的存储设备错误、网络错误或者软件bug形成的。HDFS客户端软件实现了对HDFS文件内容的校验和(checksum)检查。当客户端建立一个新的HDFS文件,会计算这个文件每一个数据块的校验和,并将校验和做为一个单独的隐藏文件保存在同一个HDFS名字空间下。当客户端获取文件内容后,它会检验从Datanode获取的数据跟相应的校验和文件中的校验和是否匹配,若是不匹配,客户端能够选择从其余Datanode获取该数据块的副本。
FsImage和Editlog是HDFS的核心数据结构。若是这些文件损坏了,整个HDFS实例都将失效。于是,Namenode能够配置成支持维护多个FsImage和Editlog的副本。任何对FsImage或者Editlog的修改,都将同步到它们的副本上。这种多副本的同步操做可能会下降Namenode每秒处理的名字空间事务数量。然而这个代价是能够接受的,由于即便HDFS的应用是数据密集的,它们也非元数据密集的。
HADOOP2.0中添加的QJM等高可用方式,进一步保证了元数据的安全性。 当其中一个Namenode节点的元数据磁盘损坏时,咱们只须要更换目录或者磁盘,将另外一个可用Namenode的元数据同步回来便可。HDFS系统在其中一个Namenode元数据损坏时依然可以对外提供服务。
快照支持某一特定时刻的数据的复制备份。利用快照,可让HDFS在数据损坏时恢复到过去一个已知正确的时间点。
快照可用于整个文件系统,也可只用于部分文件系统。快照最广泛的用途是数据备份,防止用户错误和灾难恢复。
操做系统下普通的文件系统(如ext4)都是基于数据块的,数据块是磁盘进行数据读写的最小单位。构建在磁盘上的文件系统都是使用磁盘块来管理文件系统的块,通常文件系统的大小能够是磁盘块的整数倍。
HDFS也有数据块的概念,是HDFS对于文件系统抽象出来的概念。最初默认大小是64MB,较新版本默认大小是128MB.与磁盘块的概念相似,HDFS上存储的文件也被划分红基于块大小的多个分块。可是与普通文件系统不一样的是,HDFS上的块若是小于设置的块大小时,并不会真正占用整个块的实际存储空间。
HDFS的块放的比较大,主要是为了把寻道时间最小化。若是一个块足够大,那么硬盘传输数据的时间远远大于寻找块的起始位置的时间。这样就使得HDFS的数据传输速度与硬盘的传输速度更加接近。
客户端建立文件的请求其实并无当即发送给Namenode,事实上,在刚开始阶段HDFS客户端会先将文件数据缓存到本地的一个临时文件。应用程序的写操做被透明地重定向到这个临时文件。当这个临时文件累积的数据量超过一个数据块的大小,客户端才会联系Namenode。Namenode将文件名插入文件系统的层次结构中,而且分配一个数据块给它。而后返回Datanode的标识符和目标数据块给客户端。接着客户端将这块数据从本地临时文件上传到指定的Datanode上。当文件关闭时,在临时文件中剩余的没有上传的数据也会传输到指定的Datanode上。而后客户端告诉Namenode文件已经关闭。此时Namenode才将文件建立操做提交到日志里进行存储。若是Namenode在文件关闭前宕机了,则该文件将丢失。
上述方法是对在HDFS上运行的目标应用进行认真考虑后获得的结果。这些应用须要进行文件的流式写入。若是不采用客户端缓存,因为网络速度和网络堵塞会对吞估量形成比较大的影响。这种方法并非没有先例的,早期的文件系统,好比AFS,就用客户端缓存来提升性能。为了达到更高的数据上传效率,已经放松了POSIX标准的要求。
当客户端向HDFS文件写入数据的时候,一开始是写到本地临时文件中。假设该文件的副本系数设置为3,当本地临时文件累积到一个数据块的大小时,客户端会从Namenode获取一个Datanode列表用于存放副本。而后客户端开始向第一个Datanode传输数据,第一个Datanode一小部分一小部分(4 KB)地接收数据,将每一部分写入本地仓库,并同时传输该部分到列表中第二个Datanode节点。第二个Datanode也是这样,一小部分一小部分地接收数据,写入本地仓库,并同时传给第三个Datanode。最后,第三个Datanode接收数据并存储在本地。所以,Datanode能流水线式地从前一个节点接收数据,并在同时转发给下一个节点,数据以流水线的方式从前一个Datanode复制到下一个。
HDFS给应用提供了多种访问方式。用户能够经过Java API接口访问,也能够经过C语言的封装API访问,还能够经过浏览器的方式访问HDFS中的文件。
用户能够设置回收站生效,当用户或应用程序删除某个文件时,这个文件并无马上从HDFS中删除。实际上,HDFS会将这个文件重命名转移到/trash目录。只要文件还在/trash目录中,该文件就能够被迅速地恢复。文件在/trash中保存的时间是可配置的,当超过这个时间时,Namenode就会将该文件从名字空间中删除。删除文件会使得该文件相关的数据块被释放。
注意,从用户删除文件到HDFS空闲空间的增长之间会有必定时间的延迟。
只要被删除的文件还在/trash目录中,用户就能够恢复这个文件。若是用户想恢复被删除的文件,能够浏览/trash目录找回该文件。
/trash目录与其余的目录没有什么区别,除了一点:在该目录上HDFS会应用一个特殊策略来自动删除文件。
/trash目录仅仅保存被删除文件的最后副本。回收站只能做为用户失误致使的文件恢复,时效性短,并不能做为数据安全的全量保证。
若是考虑数据全量安全性的数据备份,应考虑使用snapshot来实现。
当一个文件的副本系数被减少后,Namenode会选择过剩的副本删除。下次心跳检测时会将该信息传递给Datanode。Datanode遂即移除相应的数据块,集群中的空闲空间加大。
一样,在调用setReplication API结束和集群中空闲空间增长间会有必定的延迟。
在HADOOP2.0以前,Namenode在HDFS集群内是一个单点故障。每一个集群都有一个Namenode,若是这个Namenode变得不可用,那么HDFS整个将变得不可用,直到Namenode重启或者迁移至另外一个机器上。
这与集群的可用性在两个主要方面有冲突:
意外事件(如非计划停机)致使集群不可用,直至Namenode重启为止。
计划内维护性工做如在Namenode上进行软件或者硬件升级会致使集群停机时间。
HDFS高可用特性旨在经过运行两个冗余的Namenode来解决以上问题,经过一个主从配置来进行一个Namenode的热备。当一个机器崩溃或者须要进行维护性工做的时候,这样容许一个新Namenode的快速恢复。
在一个典型的HA集群中,配置两个独立的Namenode节点服务器。任什么时候候,一个Namenode处于Active状态,其余的处于Stanby状态。Acitve Namenode来响应集群中来自客户端的操做,而Stanby只是扮演一个slave的状态,维护足够的状态来在必要时提供一个快速故障恢复。
为了使得Standby节点可以维持来自Active节点的状态同步,两个节点之间须要一组独立的Journalnode来进行通讯。当Acitve节点上产生任何的名字空间更改,它持续的记录更改到大部分Journalnode上。Standby节点可以读取Journalnode的Edits文件,而且持续不断的监控Edit Log的改变。Standby节点会将从Jouranlnode上所见的Edits应用到本身的名字空间。在故障转移的过程当中,Standby将确认它已经从Journalnode上读取了所有的Edits更改,而后会将本身切换成Active状态。这能确保名字空间在故障转移发生时可以彻底同步。
为了提供一个快速的故障转移,Standby节点必须实时地获取集群内全部块的位置。为了实现这个,全部的Datanode同时配置了两个Namenode的位置,而且同时发送和心跳块位置信息。
对于HA集群里正确的操做,只有一个节点保持活跃,是相当重要的。不然,名字空间的状态可能会在二者之间很快出现分歧,致使数据丢失或其余不正确的结果。为了确保性能且防止所谓的“裂脑情景”,Journalnodes只容许同一时间只有一个节点执行写入操做。故障转移期间,NameNode为了切换为Acitve状态,只要简单地接管Journalnodes写入角色,这将有效地防止其余Namenode节点继续处于Active状态,这使得新Active节点可以安全地进行故障转移。
为了部署HA集群,你应该准备以下硬件资源。
两台机器用来运行你的Active和Standby节点。两个节点应具备相同的硬件,保持与非HA集群一样的硬件配置要求。
多台机器上运行你的Journalnodes。Journalnode守护进程是相对较轻,因此这些程序能够与其余Hadoop守护进程公用机器。
必须有至少3 个Journalnode守护进程,由于Edits Log的修改必须通过大多数Journalnodes。这将容许系统容忍一台journal机器的故障。你也能够运行超过3 Journalnodes,但为了真正提升故障系统能够容忍的数量,你应该运行JNS奇数,(即3,5,7,等)
当运行N journalnodes,系统能够容忍最多(n - 1)/ 2的失败和继续正常。
自动故障转移增长了两个新的组件到HDFS部署中,分别是Zookeeper和ZKFailoverController过程(简称ZKFC)。ZKFailoverController(ZKFC)是一个新的组件,做为Zookeeper客户端同时监控和管理Namenode节点的状态。Namenode节点上须要同时运行ZKFC。
Failure detection:集群中每一个Namenode持续维持一个Zookeeper会话。若是机器崩溃,Zookeeper回话将退出,这样通知其余Namenode节点触发故障转移。
Active NameNode election:Zookeeper提供了一种简单的机制来选择其中一个节点做为Active状态。若是当前Active NameNode节点崩溃,另外一个节点可能须要在Zookeeper中持有一个特殊排他锁,来代表应成为下一个Active节点。
Health monitoring: ZKFC按期对本地Namenode执行健康检查命令。只要Namenode节点及时响应一个健康情况,ZKFC则认为考虑Namenode是健康状态。若是Namenode节点已经崩溃,冻结,或进入了一个不健康的状态,健康监测将标记其为不健康。
ZooKeeper session management:当本地NameNode处于健康状态时,ZKFC将与Zookeeper一直保持open回话。若是本地Namenode节点是Active的,它还拥有一个特殊的“锁”Znode。该锁使用的是Zookeeper的“ephemeral”(短暂)节点;若是session过时,锁的节点将被自动删除。
ZooKeeper-based election:若是本地Namenode节点是健康的,而且ZKFC查看Zookeeper发现没有其余节点获取了锁znode,它会尝试获取锁。若是它成功了,那么它已经“赢得选举”,并负责运行故障转移使本地Namenode节点变为Active状态。故障转移过程相似于上述的手动故障转移:第一,先前的活动是围栏,若是必要的话,那么当地的NameNode过渡到活动状态。
在自动故障转移设计的更多细节,请参阅Apache HDFS JIRA链接到hdfs-2185设计文档。
如下是经过QJM建立HA集群或者从单点Namenode切换为HA集群的一些部署细节:
若是你将一个非HA NameNode 转换为HA集群,你应该首先运行命令“hdfs -initializeSharedEdits”,它会用本地Namenode Edits目录的数据来初始化Journalnodes。