介绍
分布式文件系统把文件分布式存储多个计算机节点上,成千上万的计算机节点构成计算机集群。
与使用多个处理器和专用高级硬件的并行化处理装置不同,目前分布式文件系统采用的计算机集群都是普通的硬件构成,这样就很大程度的降低了硬件上的开销。
分布式文件系统的结构
分布式文件系统在物理结构上是由计算机集群中的多个节点构成的,这些节点分为两类,一类是 “主节点” :Master Node(也就是NameNode), 一类是“从节点”:Slave Node (也就是DataNode)。
大规模文件系统的整体结构如上图所示(图片来源: 林子雨教授的<<大数据技术原理与应用>>课件)
HDFS设计实现以下目标
兼容廉价的硬件设备
流数据读写
大数据集的存储
强大的跨平台兼容性
HDFS特殊设计带来的局限性
不适合低延迟数据访问
HDFS是为了高吞吐数据设计的,牺牲了延迟,如果要低延迟的数据访问,HBase更适合
无法高效存储大量小文件
因为文件的元数据(文件块的存储列表,目录结构等)存储在NameNode的内存中,这就导致了整个HDFS文件系统的文件数量受限于NameNode的内存大小,
不支持多用户写入,以及无法修改文件(文件上传到HDFS上后无法修改)
HDFS核心概念
blocks(块)
HDFS中默认一个块是128MB,一个大的文件会被分成多个block,以block作为存储单位。比block小的文件不会占用整个block,只会占据实际大小,例如,一个文件大小为10MB,则在HDFS中只会占用10MB的空间,而不会是128MB.
block大的好处
可以最小化寻址开销,但是block不能设置过大,如果block设置过大的话,在mapreduce任务中,map或者reduce任务的个数如果小于集群机器数量,会使作业运行效率很低。
支持大规模文件存储
文件以块为单位进行存储,一个大规模文件可以被分拆成若干个文件块,不同的文件可以被分发到不同的节点上,因此一个文件的大小不会受到单个节点的存储容量的限制,可以远远大于网络中任意记得的存储容量。因为一个很大的文件会被分为许多块。
简化系统设计
因为文件块大小是固定的,这样可以很容易计算出一个节点可以存储多少文件块,其次,为了方便元数据的管理,元数据不需要和文件一起存储,可以由其他系统负责管理元数据
适合数据备份
每个文件块东可以冗余存储到多个节点上,大大提高系统的容错性和可用性
NameNode & DataNode
整个HDFS集群由NameNode和DataNode构成master-worker(主从)模式,NameNode和DataNode的主要功能如下表
NameNode | DataNode |
---|---|
存储元数据 | 存储文件内容 |
元数据保存在内存中 | 文件内容保存在磁盘中 |
保存文件,block,datanode之间的映射关系 | 维护了block id 到 datanode本地文件的映射关系 |
NameNode
NameNode的数据结构
在HDFS中,NameNode负责管理分布式文件系统的命名空间(Namespace),保存了两个核心的数据结构:FSImage和EditLog
- FsImage用于维护文件系统树以及文件树中所有的文件和文件夹的元数据
- EditLog是操作日志文件,记录了所有针对文件的创建,删除,重命名等操作
FsImage文件
FsImage文件包含文件系统中所有目录和文件inode的序列化形式,每个inode是一个文件或目录的元数据的内部表示,并且包含了这些信息:文件的复制等级、修改和访问 时间、访问权限、块大小以及组成文件的块。对于目录,则存储修改时间、权限和配 额元数据
FsImage文件没有记录块存储在哪个数据节点。而是由NameNode把这些映射保留在 内存中,当数据节点加入HDFS集群时,DataNode会把自己所包含的块列表告知给NameNode,此后会定期执行这种告知操作,以确保NameNode的块映射是最新的。
NameNode的启动过程
- 在NameNode启动的时候,它会将FsImage文件中的内容加载到内存中,之后再执行 EditLog文件中的各项操作,使得内存中的元数据和实际的同步,存在内存中的元数据支持客户端的读操作。
- 一旦在内存中成功建立文件系统元数据的映射,则创建一个新的FsImage文件和一个空的EditLog文件
- NameNode运行起来后,HDFS中的更新操作会重新写到EditLog文件中,而不是添加到FsImage中,因为FsImage文件一般都很大(GB级别的是常见),如果所有的操作都往FsImage中添加的会导致系统运行的十分缓慢。 但是,如果往EditLog文件里面写就不会这样 ,因为EditLog 要小很多。每次执行写操作之后,且在向客户端发送成功代码之前, edits文件(就是Edit Log)都需要同步更新
PS:由NameNode的启动过程可知,FsImage只有在启动时会改变,其他时间都是静态不变的,用户操作时改变的是EditLog文件。
名称节点运行期间EditLog不断变大的问题
在名称节点运行期间,HDFS的所有更新操作都是直接写到EditLog中,久而久之, EditLog文件将会变得很大,虽然在NameNode运行期间没有影响,但是当NameNode重启时,NameNode需要将FsImage里面的所有内容映像到内存中,然后再一条条的执行EditLog中的记录,当 EditLog文件非常大的时候,会导致NameNode启动操作非常慢,而在这段时间内HDFS系统处于 安全模式,一直无法对外提供写操作,影响了用户的使用
解决方法
通过 SecondaryNameNode 对FsImage和editLog文件的合并
第二名称节点是HDFS架构中的一个组成部分,它是用来保存名称节点中对HDFS 元数据信息的备份,并减少名称节点重启的时间。SecondaryNameNode一般是单独运 行在一台机器上。
合并的流程图如下
- SecondaryNameNode会定期和NameNode 通信,请求其停止使用EditLog文件,暂时将新的写操作写到一个新的文件edit.new上来,这个操 作是瞬间完成,上层写日志的函数完全感觉不到差别;
- SecondaryNameNode通过HTTP GET 方式从NameNode上获取到FsImage和EditLog文 件,并下载到本地的相应目录下;
- SecondaryNameNode将下载下来的 FsImage载入到内存,然后一条一条地执行 EditLog文件中的各项更新操作,使得内存中的 FsImage 保持最新;这个过程就是EditLog和 FsImage文件合并,合并后FsImage的文件就是最新的了
- SecondaryNameNode执行完合并操作之后,会通过post方式将新的FsImage文件发 送到NameNode节点上
- NameNode将从SecondaryNameNode 接收到的新的FsImage替换旧的FsImage文件, 同时将edit.new替换EditLog文件,通过这个过程 EditLog就变小了。(Edit Log文件不只一个)
DataNode
- 数据节点是分布式文件系统HDFS的工作节点,负责数据的存储和读取,会根据客户端或者是名称节点的调度来进行数据的存储和检索,并且向NameNode定期发送自己 所存储的块的列表
- 每个数据节点中的数据会被保存在各自节点的本地Linux文件系统中
Block Caching
DataNode通常直接从磁盘读取数据,但是对频繁使用的Block块可以在内存中缓存,默认情况下,一个Block只有一个DataNode会缓存
作业调度器可以利用缓存提升性能,例如MapReduce可以把任务运行在有Block缓存的节点上。
用户或者应用可以向NameNode发送缓存指令(缓存哪个文件,缓存多久), 缓存池的概念用于管理一组缓存的权限和资源。
HDFS Federation
我们知道NameNode的内存会制约文件数量(元数据存在内存中),HDFS Federation提供了一种横向扩展NameNode的方式。在Federation模式中,每个NameNode管理命名空间的一部分,例如一个NameNode管理/user目录下的文件, 另一个NameNode管理/share目录下的文件。
每个NameNode管理一个namespace volumn,所有volumn构成文件系统的元数据。每个NameNode同时维护一个Block Pool,保存Block的节点映射等信息。各NameNode之间是独立的,一个节点的失败不会导致其他节点管理的文件不可用。
HDFS HA*
在HDFS集群中,NameNode依然是单点故障(SPOF)。元数据同时写到多个文件系统以及Second NameNode定期checkpoint有利于保护数据丢失,但是并不能提高可用性。
这是因为NameNode是唯一一个对文件元数据和file-block映射负责的地方, 当它挂了之后,包括MapReduce在内的作业都无法进行读写。
当NameNode故障时,常规的做法是使用元数据备份重新启动一个NameNode。元数据备份可能来源于:
多文件系统写入中的备份
Second NameNode的检查点文件
启动新的Namenode之后,需要重新配置客户端和DataNode的NameNode信息。另外重启耗时一般比较久,稍具规模的集群重启经常需要几十分钟甚至数小时,造成重启耗时的原因大致有:
1) 元数据镜像文件载入到内存耗时较长。
2) 需要重放edit log
3) 需要收到来自DataNode的状态报告并且满足条件后才能离开安全模式提供写服务。
Hadoop的HA方案*
采用HA的HDFS集群配置两个NameNode,分别处于Active和Standby状态。当Active NameNode故障之后,Standby接过责任继续提供服务,用户没有明显的中断感觉。一般耗时在几十秒到数分钟,不像冷备份,恢复时间较长,恢复期间用户是无法访问。
HA涉及到的主要实现逻辑有
1) 主备需共享edit log存储。
Active状态的NameNode和Standby状态的NameNode共享一份edit log,当主备切换时,Standby通过回放edit log同步数据。
共享存储通常有2种选择
NFS:传统的网络文件系统
QJM:quorum journal manager
QJM是专门为HDFS的HA实现而设计的,用来提供高可用的edit log。QJM运行一组journal node,edit log必须写到大部分的journal nodes。通常使用3个节点,因此允许一个节点失败,类似ZooKeeper。注意QJM没有使用ZK,虽然HDFS HA的确使用了ZK来选举主Namenode。一般推荐使用QJM。
2)DataNode需要同时往主备发送Block Report
因为Block映射数据存储在内存中(不是在磁盘上),为了在Active NameNode挂掉之后,新的NameNode能够快速启动,不需要等待来自Datanode的Block Report,DataNode需要同时向主备两个NameNode发送Block Report。
3)客户端需要配置failover模式(对用户透明)
Namenode的切换对客户端来说是无感知的,通过客户端库来实现。客户端在配置文件中使用的HDFS URI是逻辑路径,映射到一对Namenode地址。客户端会不断尝试每一个Namenode地址直到成功。
4)Standby替代Secondary NameNode
如果没有启用HA,HDFS独立运行一个守护进程作为Secondary Namenode(第二名称节点)。定期checkpoint,合并镜像文件和edit日志。
如果当主Namenode失败时,备份Namenode正在关机(停止 Standby),运维人员依然可以从头启动备份Namenode,这样比没有HA的时候更省事,算是一种改进,因为重启整个过程已经标准化到Hadoop内部,无需运维进行复杂的切换操作。
NameNode的切换通过代failover controller来实现。failover controller有多种实现,默认实现使用ZooKeeper来保证只有一个Namenode处于active状态。
每个Namenode运行一个轻量级的failover controller进程,该进程使用简单的心跳机制来监控Namenode的存活状态并在Namenode失败是触发failover。Failover可以由运维手动触发,例如在日常维护中需要切换主Namenode,这种情况graceful failover,非手动触发的failover称为ungraceful failover。
在ungraceful failover的情况下,没有办法确定失败(被判定为失败)的节点是否停止运行,也就是说触发failover后,之前的主Namenode可能还在运行。QJM一次只允许一个Namenode写edit log,但是之前的主Namenode仍然可以接受读请求。Hadoop使用fencing来杀掉之前的Namenode。Fencing通过收回之前Namenode对共享的edit log的访问权限、关闭其网络端口使得原有的Namenode不能再继续接受服务请求。使用STONITH技术也可以将之前的主Namenode关机。
最后,HA方案中Namenode的切换对客户端来说是不可见的
通信协议
- 所有的HDFS通信协议都是构建在 TCP/IP协议基础之上
- 客户端通过一个可配置的端口向NameNode发起TCP连接,并使用客户端协议与NameNode进行交互
- NameNode与DataNode之间使用数据节点协议(DatanodeProtocol)进行交互
- 客户端与数据节点的交互是通过RPC(Remote Procedure Call)来实现的。在设计上,NameNode不会主动发起RPC,而是响应来自客户端和DataNode的RPC请求
HDFS存储原理
冗余数据存储
作为一个分布式文件系统,为了保证系统的容错性和可用性,HDFS采用了多副本方式对数据进行冗余存储,通常一个数据块的多个副本会被分布到不同的数据节点 上,如图所示,数据块1被分别存放到数据节点A和C上,数据块2被存放在数据节 点A和B上。这种多副本方式具有以下几个优点:
数据存取策略
- 第一个副本(Block1):放置在上传文件的数据节点;如果是集群外提交,则随机挑选一台磁盘 不太满、CPU不太忙的节点
- 第二个副本(Block2):放置在与第一个副本不同的机架的节点上
- 第三个副本(Block3):与第一个副本相同机架的其他节点上
- 更多副本:随机节点
DataNode出错
- 每个数据节点会定期向名称节点发送“心跳”信息,向名称节点报告自己的状态 (心跳机制)
- 当DataNode发生故障,或者网络发生断网时,NameNode就无法收到来自一些DataNode的心跳信息,这时,这些DataNode就会被标记为“宕机”,节点上面的所有数据都会被标记为“不可读”,NameNode不会再给它们发送任何I/O请求
- 这时,有可能出现一种情形,即由于一些DataNode的不可用,会导致一些数据块的 副本数量小于冗余因子(设置的副本数量) ,NameNode会定期检查这种情况,一旦发现某个数据块的副本数量小于冗余因子,就 会启动数据冗余复制,为它生成新的副本
- HDFS和其它分布式文件系统的最大区别就是可以调整冗余数据的位置
参考:
https://blog.youkuaiyun.com/bingduanlbd/article/details/51914550