深刻理解一个技术的工做机制是灵活运用和快速解决问题的根本方法,也是惟一途径。对于HDFS来讲除了要明白它的应用场景和用法以及通用分布式架构以外更重要的是理解关键步骤的原理和实现细节。在看这篇博文以前须要对HDFS以及分布式系统有一些了解。请参考这篇博客。本篇博文首先对HDFS的重要特性和使用场景作一个简要说明,以后对HDFS的数据读写、元数据管理以及NameNode、SecondaryNamenode的工做机制进行深刻分析。过程当中也会对一些配置参数作一个说明。html
First. HDFS是一个文件系统,用于存储和管理文件,经过统一的命名空间(相似于本地文件系统的目录树)。是分布式的,服务器集群中各个节点都有本身的角色和职责。node
Then.apache
1.HDFS中的文件在物理上是分块存储(block),块的大小能够经过配置参数( dfs.blocksize)来规定,默认大小在hadoop2.x版本中是128M,以前的版本中是64M。缓存
2.HDFS文件系统会给客户端提供一个统一的抽象目录树,客户端经过路径来访问文件,形如:hdfs://namenode:port/dir-a/dir-b/dir-c/file.data安全
3.目录结构及文件分块位置信息(元数据)的管理由namenode节点承担,namenode是HDFS集群主节点,负责维护整个hdfs文件系统的目录树,以及每个路径(文件)所对应的数据块信息(blockid及所在的datanode服务器)服务器
4.文件的各个block的存储管理由datanode节点承担,datanode是HDFS集群从节点,每个block均可以在多个datanode上存储多个副本(副本数量也能够经过参数设置dfs.replication,默认是3)网络
5.Datanode会按期向Namenode汇报自身所保存的文件block信息,而namenode则会负责保持文件的副本数量,HDFS的内部工做机制对客户端保持透明,客户端请求访问HDFS都是经过向namenode申请来进行。数据结构
6.HDFS是设计成适应一次写入,屡次读出的场景,且不支持文件的修改。须要频繁的RPC交互,写入性能很差。架构
1.概述负载均衡
客户端要向HDFS写数据,首先要跟namenode通讯以确承认以写文件并得到接收文件block的datanode,而后客户端按顺序将文件逐个block传递给相应datanode,并由接收到block的datanode负责向其余datanode复制block的副本。
2.写数据步骤详解
(图片来自网络,仅供参考)
1)客户端向namenode发送上传文件请求,namenode对要上传目录和文件进行检查,判断是否能够上传,并向客户端返回检查结果。
2)客户端获得上传文件的容许后读取客户端配置,若是没有指定配置则会读取默认配置(例如副本数和块大小默认为3和128M,副本是由客户端决定的)。向namenode请求上传一个数据块。
3)namenode会根据客户端的配置来查询datanode信息,若是使用默认配置,那么最终结果会返回同一个机架的两个datanode和另外一个机架的datanode。这称为“机架感知”策略。
机架感知:HDFS采用一种称为机架感知(rack-aware)的策略来改进数据的可靠性、可用性和网络带宽的利用率。大型HDFS实例通常运行在跨越多个机架的计算机组成的集群上,不一样机架上的两台机器之间的通信须要通过交换机。在大多数状况下,同一个机架内的两台机器间的带宽会比不一样机架的两台机器间的带宽大。经过一个机架感知的过程,Namenode能够肯定每一个Datanode所属的机架id。一个简单但没有优化的策略就是将副本存放在不一样的机架上。这样能够有效防止当整个机架失效时数据的丢失,而且容许读数据的时候充分利用多个机架的带宽。这种策略设置能够将副本均匀分布在集群中,有利于当组件失效状况下的负载均衡。可是,由于这种策略的一个写操做须要传输数据块到多个机架,这增长了写的代价。在大多数状况下,副本系数是3,HDFS的存放策略是将一个副本存放在本地机架的节点上,一个副本放在同一机架的另外一个节点上,最后一个副本放在不一样机架的节点上。这种策略减小了机架间的数据传输,这就提升了写操做的效率。机架的错误远远比节点的错误少,因此这个策略不会影响到数据的可靠性和可用性。于此同时,由于数据块只放在两个(不是三个)不一样的机架上,因此此策略减小了读取数据时须要的网络传输总带宽。在这种策略下,副本并非均匀分布在不一样的机架上。三分之一的副本在一个节点上,三分之二的副本在一个机架上,其余副本均匀分布在剩下的机架中,这一策略在不损害数据可靠性和读取性能的状况下改进了写的性能。
4)客户端在开始传输数据块以前会把数据缓存在本地,当缓存大小超过了一个数据块的大小,客户端就会从namenode获取要上传的datanode列表。以后会在客户端和第一个datanode创建链接开始流式的传输数据,这个datanode会一小部分一小部分(4K)的接收数据而后写入本地仓库,同时会把这些数据传输到第二个datanode,第二个datanode也一样一小部分一小部分的接收数据并写入本地仓库,同时传输给第三个datanode,依次类推。这样逐级调用和返回以后,待这个数据块传输完成客户端后告诉namenode数据块传输完成,这时候namenode才会更新元数据信息记录操做日志。
5)第一个数据块传输完成后会使用一样的方式传输下面的数据块直到整个文件上传完成。
细节:
a.请求和应答是使用RPC的方式,客户端经过ClientProtocol与namenode通讯,namenode和datanode之间使用DatanodeProtocol交互。在设计上,namenode不会主动发起RPC,而是响应来自客户端或 datanode 的RPC请求。客户端和datanode之间是使用socket进行数据传输,和namenode之间的交互采用nio封装的RPC。
b.HDFS有本身的序列化协议。
c.在数据块传输成功后但客户端没有告诉namenode以前若是namenode宕机那么这个数据块就会丢失。
d.在流式复制时,逐级传输和响应采用响应队列来等待传输结果。队列响应完成后返回给客户端。
c.在流式复制时若是有一台或两台(不是所有)没有复制成功,不影响最后结果,只不过datanode会按期向namenode汇报自身信息。若是发现异常namenode会指挥datanode删除残余数据和完善副本。若是副本数量少于某个最小值就会进入安全模式。
安全模式:Namenode启动后会进入一个称为安全模式的特殊状态。处于安全模式的Namenode是不会进行数据块的复制的。Namenode从全部的 Datanode接收心跳信号和块状态报告。块状态报告包括了某个Datanode全部的数据块列表。每一个数据块都有一个指定的最小副本数。当Namenode检测确认某个数据块的副本数目达到这个最小值,那么该数据块就会被认为是副本安全(safely replicated)的;在必定百分比(这个参数可配置)的数据块被Namenode检测确认是安全以后(加上一个额外的30秒等待时间),Namenode将退出安全模式状态。接下来它会肯定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其余Datanode上。
1.概述
客户端将要读取的文件路径发送给namenode,namenode获取文件的元信息(主要是block的存放位置信息)返回给客户端,客户端根据返回的信息找到相应datanode逐个获取文件的block并在客户端本地进行数据追加合并从而得到整个文件。
2.读数据步骤详解
(图片来源于网络,仅供参考)
1)客户端向namenode发起RPC调用,请求读取文件数据。
2)namenode检查文件是否存在,若是存在则获取文件的元信息(blockid以及对应的datanode列表)。
3)客户端收到元信息后选取一个网络距离最近的datanode,依次请求读取每一个数据块。客户端首先要校检文件是否损坏,若是损坏,客户端会选取另外的datanode请求。
4)datanode与客户端简历socket链接,传输对应的数据块,客户端收到数据缓存到本地,以后写入文件。
5)依次传输剩下的数据块,直到整个文件合并完成。
从某个Datanode获取的数据块有多是损坏的,损坏多是由Datanode的存储设备错误、网络错误或者软件bug形成的。HDFS客户端软件实现了对HDFS文件内容的校验和(checksum)检查。当客户端建立一个新的HDFS文件,会计算这个文件每一个数据块的校验和,并将校验和做为一个单独的隐藏文件保存在同一个HDFS名字空间下。当客户端获取文件内容后,它会检验从Datanode获取的数据跟相应的校验和文件中的校验和是否匹配,若是不匹配,客户端能够选择从其余Datanode获取该数据块的副本。
HDFS删除数据比较流程相对简单,只列出详细步骤:
1)客户端向namenode发起RPC调用,请求删除文件。namenode检查合法性。
2)namenode查询文件相关元信息,向存储文件数据块的datanode发出删除请求。
3)datanode删除相关数据块。返回结果。
4)namenode返回结果给客户端。
当用户或应用程序删除某个文件时,这个文件并无马上从HDFS中删除。实际上,HDFS会将这个文件重命名转移到/trash目录。只要文件还在/trash目录中,该文件就能够被迅速地恢复。文件在/trash中保存的时间是可配置的,当超过这个时间时,Namenode就会将该文件从名字空间中删除。删除文件会使得该文件相关的数据块被释放。注意,从用户删除文件到HDFS空闲空间的增长之间会有必定时间的延迟。只要被删除的文件还在/trash目录中,用户就能够恢复这个文件。若是用户想恢复被删除的文件,他/她能够浏览/trash目录找回该文件。/trash目录仅仅保存被删除文件的最后副本。/trash目录与其余的目录没有什么区别,除了一点:在该目录上HDFS会应用一个特殊策略来自动删除文件。目前的默认策略是删除/trash中保留时间超过6小时的文件。未来,这个策略能够经过一个被良好定义的接口配置。
当一个文件的副本系数被减少后,Namenode会选择过剩的副本删除。下次心跳检测时会将该信息传递给Datanode。Datanode遂即移除相应的数据块,集群中的空闲空间加大。一样,在调用setReplication API结束和集群中空闲空间增长间会有必定的延迟。
1.概述
首先明确namenode的职责:响应客户端请求、管理元数据。
namenode对元数据有三种存储方式:
内存元数据(NameSystem)
磁盘元数据镜像文件
数据操做日志文件(可经过日志运算出元数据)
细节:HDFS不适合存储小文件的缘由,每一个文件都会产生元信息,当小文件多了以后元信息也就多了,对namenode会形成压力。
2.对三种存储机制的进一步解释
内存元数据就是当前namenode正在使用的元数据,是存储在内存中的。
磁盘元数据镜像文件是内存元数据的镜像,保存在namenode工做目录中,它是一个准元数据,做用是在namenode宕机时可以快速较准确的恢复元数据。称为fsimage。
数据操做日志文件是用来记录元数据操做的,在每次改动元数据时都会追加日志记录,若是有完整的日志就能够还原完整的元数据。主要做用是用来完善fsimage,减小fsimage和内存元数据的差距。称为editslog。
3.checkpoint机制分析
由于namenode自己的任务就很是重要,为了避免再给namenode压力,日志合并到fsimage就引入了另外一个角色secondarynamenode。secondarynamenode负责按期把editslog合并到fsimage,“按期”是namenode向secondarynamenode发送RPC请求的,是按时间或者日志记录条数为“间隔”的,这样即不会浪费合并操做又不会形成fsimage和内存元数据有很大的差距。由于元数据的改变频率是不固定的。
每隔一段时间,会由secondary namenode将namenode上积累的全部edits和一个最新的fsimage下载到本地,并加载到内存进行merge(这个过程称为checkpoint)。
(图片来源于网络,仅供参考)
1)namenode向secondarynamenode发送RPC请求,请求合并editslog到fsimage。
2)secondarynamenode收到请求后从namenode上读取(经过http服务)editslog(多个,滚动日志文件)和fsimage文件。
3)secondarynamenode会根据拿到的editslog合并到fsimage。造成最新的fsimage文件。(中间有不少步骤,把文件加载到内存,还原成元数据结构,合并,再生成文件,新生成的文件名为fsimage.checkpoint)。
4)secondarynamenode经过http服务把fsimage.checkpoint文件上传到namenode,而且经过RPC调用把文件更名为fsimage。
namenode和secondary namenode的工做目录存储结构彻底相同,因此,当namenode故障退出须要从新恢复时,能够从secondary namenode的工做目录中将fsimage拷贝到namenode的工做目录,以恢复namenode的元数据。
关于checkpoint操做的配置:
dfs.namenode.checkpoint.check.period=60 #检查触发条件是否知足的频率,60秒
dfs.namenode.checkpoint.dir=file://${hadoop.tmp.dir}/dfs/namesecondary
#以上两个参数作checkpoint操做时,secondary namenode的本地工做目录
dfs.namenode.checkpoint.edits.dir=${dfs.namenode.checkpoint.dir}
dfs.namenode.checkpoint.max-retries=3 #最大重试次数
dfs.namenode.checkpoint.period=3600 #两次checkpoint之间的时间间隔3600秒
dfs.namenode.checkpoint.txns=1000000 #两次checkpoint之间最大的操做记录
editslog和fsimage文件存储在$dfs.namenode.name.dir/current目录下,这个目录能够在hdfs-site.xml中配置的。这个目录下的文件结构以下:
包括edits日志文件(滚动的多个文件),有一个是edits_inprogress_*是当前正在写的日志。fsimage文件以及md5校检文件。seen_txid是记录当前滚动序号,表明seen_txid以前的日志都已经合并完成。
$dfs.namenode.name.dir/current/seen_txid很是重要,是存放transactionId的文件,format以后是0,它表明的是namenode里面的edits_*文件的尾数,namenode重启的时候,会按照seen_txid的数字恢复。因此当你的hdfs发生异常重启的时候,必定要比对seen_txid内的数字是否是你edits最后的尾数,否则会发生重启namenode时metaData的资料有缺乏,致使误删Datanode上多余Block的信息。
深刻理解了以上介绍的工做机制就能够尝试运用他们解决工做和学习中遇到的问题了,只要真正理解了核心原理,全部问题均可以本身找到答案。就是要不断的学习、实践、总结,再学习、再实践、再总结。这样才能扎扎实实作的出色。共勉。
接下来会有一篇HDFS常见问题的总结。
参考资料:http://hadoop.apache.org/docs/stable2/hadoop-project-dist/hadoop-hdfs/HdfsUserGuide.html