HDFS基础

一、数据存储

1.1 HDFS基础架构

namenode

namenode对元数据有三种存储方式:内存元数据(NameSystem)  磁盘元数据镜像文件  数据操作日志文件(可通过日志运算出元数据)

namenode中的元数据如果存储在namenode节点的磁盘中,因为经常需要进行随机访问,还有响应客户请求,必然是效率过低。因此,元数据需要存放在内存中。但如果只存在内存中,一旦断点,元数据丢失,整个集群就无法工作了!!!因此必须在磁盘中有备份,在磁盘中的备份就是fsImage,存放在namenode节点对应的磁盘中。这样又会带来新的问题,当在内存中的元数据更新时,如果同时更新fsImage,就会导致效率过低,但如果不更新,就会发生一致性问题,一旦namenode节点断点,就会产生数据丢失。因此,引入edits.log文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加到edits.log中。这样,一旦namenode节点断电,可以通过fsImage和edits.log的合并,合成元数据。但是,如果长时间添加数据到edit.log中,会导致该文件数据过大,效率降低,而且一旦断电,恢复元数据需要的时间过长。因此,需要定期进行fsImage和edits.log的合并,如果这个操作如果由namenode节点完成,又会效率过低。因此,引入一个新的节点secondaryNamenode,专门用于fsImage和edits.log的合并。为了避免editlog不断增大,secondary namenode会周期性合并fsimage和edits成新的fsimage

fsimage保存了最新的元数据检查点,包含了整个HDFS文件系统的所有目录和文件的信息。对于文件来说包括了数据块描述信息、修改时间、访问时间等;对于目录来说包括修改时间、访问权限控制信息(目录所属用户,所在组)等。

editlog主要是在NameNode已经启动情况下对HDFS进行的各种更新操作进行记录,HDFS客户端执行所有的写操作都会被记录到editlog中。

简单来想,NameNode维护了文件与数据块的映射表以及数据块与数据节点的映射表,什么意思呢?就是一个文件,它切分成了几个数据块,以及这些数据块分别存储在哪些datanode上,namenode一清二楚。Fsimage就是在某一时刻,整个hdfs 的快照,就是这个时刻hdfs上所有的文件块和目录,分别的状态,位于哪些个datanode,各自的权限,各自的副本个数。然后客户端对hdfs所有的更新操作,比如说移动数据,或者删除数据,都会记录在editlog中。

此外,NameNode还保存了一个文件包括哪些数据块,分布在哪些数据节点上。然而,这些信息不存放在硬盘上,而是在系统启动的时候从数据节点收集而成的。--这正是无法存储大量小文件的原因

Namenode在内存中保存着整个文件系统的名字空间和文件数据块映射(Blockmap)的映像。这个关键的元数据结构设计得很紧凑,因而一个有4G内存的Namenode足够支撑大量的文件和目录。当Namenode启动时,它从硬盘中读取Editlog和FsImage,将所有Editlog中的事务作用在内存中的FsImage上,并将这个新版本的FsImage从内存中保存到本地磁盘上,然后删除旧的Editlog,因为这个旧的Editlog的事务都已经作用在FsImage上了。这个过程称为一个检查点(checkpoint)。在当前实现中,检查点只发生在Namenode启动时,在不久的将来将实现支持周期性的检查点。

DataNode:数据节点。是HDFS真正存储数据的地方。客户端(client)和元数据节点(NameNode)可以向数据节点请求写入或者读出数据块。此外,DataNode需要周期性的向元数据节点回报其存储的数据块信息。

一旦有datanode挂掉了(宕机或者是网络阻塞),namenode能很快感知到,并且将宕机的节点上的数据块转移至其余空闲节点。这点是因为hdfs中心跳机制(heartbeat)。

心跳机制默认3s中一次,datanode会向namenode发送一次一跳,告知namenode当前节点上存放的数据文件是什么。如果namenode中记录的是该datanode存放了文件A的两个数据块和文件B的一个数据块,但是心跳中只有文件A的一个数据块信息,namenode就会知道该datanode数据块损坏了,会把损坏的数据块在别的datanode上补充。

Secondary NameNode:从元数据节点并不是NameNode出现问题时候的备用节点,它的主要功能是周期性的将NameNode中的namespace image和edit log合并,以防log文件过大。此外,合并过后的namespace image文件也会在Secondary NameNode上保存一份,以防NameNode失败的时候,可以恢复。--利用Secondary NameNode中的备份文件来恢复NameNode

1.2 HDFS写数据

 1)客户端向namenode发送上传文件请求,namenode对要上传目录和文件进行检查,判断是否可以上传,并向客户端返回检查结果。

  2)客户端得到上传文件的允许后读取客户端配置,如果没有指定配置则会读取默认配置(例如副本数和块大小默认为3和128M,副本是由客户端决定的)。向namenode请求上传一个数据块。

  3)namenode会根据客户端的配置来查询datanode信息,如果使用默认配置,那么最终结果会返回同一个机架的两个datanode和另一个机架的datanode。这称为“机架感知”策略。

4)客户端在开始传输数据块之前会把数据缓存在本地,当缓存大小超过了一个数据块的大小,客户端就会从namenode获取要上传的datanode列表。之后会在客户端和第一个datanode建立连接开始流式的传输数据,这个datanode会一小部分一小部分(4K)的接收数据然后写入本地仓库,同时会把这些数据传输到第二个datanode,第二个datanode也同样一小部分一小部分的接收数据并写入本地仓库,同时传输给第三个datanode,依次类推。这样逐级调用和返回之后,待这个数据块传输完成客户端后告诉namenode数据块传输完成,这时候namenode才会更新元数据信息记录操作日志。

  5)第一个数据块传输完成后会使用同样的方式传输下面的数据块直到整个文件上传完成。

机架感知:HDFS采用一种称为机架感知(rack-aware)的策略来改进数据的可靠性、可用性和网络带宽的利用率。大型HDFS实例一般运行在跨越多个机架的计算机组成的集群上,不同机架上的两台机器之间的通讯需要经过交换机。在大多数情况下,同一个机架内的两台机器间的带宽会比不同机架的两台机器间的带宽大。通过一个机架感知的过程,Namenode可以确定每个Datanode所属的机架id。一个简单但没有优化的策略就是将副本存放在不同的机架上。这样可以有效防止当整个机架失效时数据的丢失,并且允许读数据的时候充分利用多个机架的带宽。这种策略设置可以将副本均匀分布在集群中,有利于当组件失效情况下的负载均衡。但是,因为这种策略的一个写操作需要传输数据块到多个机架,这增加了写的代价。在大多数情况下,副本系数是3,HDFS的存放策略是将一个副本存放在本地机架的节点上,一个副本放在同一机架的另一个节点上,最后一个副本放在不同机架的节点上。这种策略减少了机架间的数据传输,这就提高了写操作的效率。机架的错误远远比节点的错误少,所以这个策略不会影响到数据的可靠性和可用性。于此同时,因为数据块只放在两个(不是三个)不同的机架上,所以此策略减少了读取数据时需要的网络传输总带宽。在这种策略下,副本并不是均匀分布在不同的机架上。三分之一的副本在一个节点上,三分之二的副本在一个机架上,其他副本均匀分布在剩下的机架中,这一策略在不损害数据可靠性和读取性能的情况下改进了写的性能。

客户端创建文件的请求其实并没有立即发送给Namenode,事实上,在刚开始阶段HDFS客户端会先将文件数据缓存到本地的一个临时文件。应用程序的写操作被透明地重定向到这个临时文件。当这个临时文件累积的数据量超过一个数据块的大小,客户端才会联系Namenode。Namenode将文件名插入文件系统的层次结构中,并且分配一个数据块给它。然后返回Datanode的标识符和目标数据块给客户端。接着客户端将这块数据从本地临时文件上传到指定的Datanode上。当文件关闭时,在临时文件中剩余的没有上传的数据也会传输到指定的Datanode上。然后客户端告诉Namenode文件已经关闭。此时Namenode才将文件创建操作提交到日志里进行存储。如果Namenode在文件关闭前宕机了,则该文件将丢失。

上述方法是对在HDFS上运行的目标应用进行认真考虑后得到的结果。这些应用需要进行文件的流式写入。如果不采用客户端缓存,由于网络速度和网络堵塞会对吞估量造成比较大的影响。这种方法并不是没有先例的,早期的文件系统,比如AFS,就用客户端缓存来提高性能。为了达到更高的数据上传效率,已经放松了POSIX标准的要求。

1.3 HDFS读数据

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.安全模式

HDFS刚刚启动时,NameNode会进入安全模式(safe mode)。处于安全模式的NameNode不能做任何的文件操作,甚至内部的副本创建也是不允许的。NameNode此时需要和各个DataNode通信,获得DataNode保存的数据块信息,并对数据块信息进行检查。只有通过了NameNode的检查,一个数据块才被认为是安全的。当认为安全的数据块所占的比例达到了某个阈值(可配置),NameNode才会退出。

2.SecondaryNameNode

Hadoop中使用SecondaryNameNode来备份NameNode的元数据,以便在NameNode失效时能从SecondaryNameNode恢复出NameNode上的元数据。SecondaryNameNode充当NameNode的一个副本,它本身并不处理任何请求,因为处理这些请求都是NameNode的责任。

NameNode中保存了整个文件系统的元数据,而SecondaryNameNode的作用就是周期性(周期的长短也是可以配置的)保存NameNode的元数据。这些元数据中包括文件镜像数据FsImage和编辑日志数据EditLog。FsImage相当于HDFS的检查点,NameNode启动时候会读取FsImage的内容到内存,并将其与EditLog日志中的所有修改信息合并生成新的FsImage;在NameNode运行过程中,所有关于HDFS的修改都将写入EditLog。这样,如果NameNode失效,可以通过Secondary NameNode中保存的FsImage和EditLog数据恢复出NameNode最近的状态,尽量减少损失。

3.心跳包(HeartBeats)和副本重新创建(re-replication)

如果HDFS运行过程中,一部分DataNode因为崩溃或是掉线等原因,离开了HDFS系统,怎么办?为了保证NameNode和各个DataNode的联系,HDFS采用了心跳包(Heart beat)机制。位于整个HDFS核心的NameNode,通过周期性的活动来检查DataNode的活性,就像跳动的心脏一样,所以,这里把这些包就叫做心跳包。NameNode周期性向管理的各个DataNode发送心跳包,而收到心跳包的DataNode则需要回复。因为心跳包总是定时发送的,所以NameNode就把要执行的命令也通过心跳包发送给DataNode,而DataNode收到心跳包,一方面回复NameNode,另一方面就开始了与用户或者应用的数据传输。

如果侦测到了DataNode失效,那么之前保存在这个DataNode上的数据就变成不可用的。那么,如果有的副本存储在失效的DataNode上,则需要重新创建这个副本,放到另外可用的地方。其他需要创建副本的情况包括数据块校验失败等。

4.数据一致性

一般来讲,DataNode与应用数据交互的大部分情况都是通过网络进行的,而网络数据传输带来的一大问题就是数据是否能原样到达。为了保证数据的一致性,HDFS采用了数据校验和(CheckSum)机制。创建文件时,HDFS会为这个文件生成一个校验和,校验和文件和文件本身保存在同一空间中。传输数据时会将数据与校验和一起传输,应用收到数据后可以进行校验,如果两个校验的结果不同,则文件肯定出错了,这个数据块就变成了无效的。如果判定数据无效,就需要从其他DataNode上读取副本。

5.租约

Linux中,为了防止出现多个进程向同一个文件写数据的情况,采用了文件加锁的机制。而在HDFS中,同样也需要一种机制来防止同一个文件被多个人写入数据。这种机制就是租约(Lease)。每当写入文件之前,一个客户端必须要获得NameNode发放的一个租约。NameNode保证同一个文件只会发放一个允许写的租约,那么就可以有效防止出现多人写入的情况。

不过,租约的作用不止于此。如果NameNode发放租约之后崩溃了,怎么办?或者如果客户端获得租约之后崩溃了,又怎么办?第一个问题可以通过前面提到的恢复机制解决。而第二个问题,则通过在租约中加入时间限制来解决。每当租约要到期时,客户端需要向NameNode申请更新租约,NameNode“审核”之后,重新发放租约。如果客户端不申请,那就说明客户端不需要读写这一文件或者已经崩溃了,NameNode收回租约即可。

6.回滚

HDFS与Hadoop一样处于发展阶段。而某个升级可能会导致BUG或者不兼容的问题,这些问题还可能导致现有的应用运行出错。这一问题可以通过回滚回到旧版本解决。HDFS安装或者升级时,会将当前的版本信息保存起来,如果升级之后一段时间内运行正常,可以认为这次升级没有问题,重新保存版本信息,否则,根据保存的旧版本信息,将HDFS恢复至之前的版本。


三、大数据吞吐

参考:Hadoop分布式文件系统:架构和设计 http://hadoop.apache.org/docs/r1.0.4/cn/hdfs_design.html

关于分布式系统的数据一致性问题 https://blog.csdn.net/moonpure/article/details/52784645

流式数据访问

运行在HDFS上的应用和普通的应用不同,需要流式访问它们的数据集。HDFS的设计中更多的考虑到了数据批处理,而不是用户交互处理。比之数据访问的低延迟问题,更关键的在于数据访问的高吞吐量。POSIX标准设置的很多硬性约束对HDFS应用系统不是必需的。为了提高数据的吞吐量,在一些关键方面对POSIX的语义做了一些修改。

大规模数据集

运行在HDFS上的应用具有很大的数据集。HDFS上的一个典型文件大小一般都在G字节至T字节。因此,HDFS被调节以支持大文件存储。它应该能提供整体上高的数据传输带宽,能在一个集群里扩展到数百个节点。一个单一的HDFS实例应该能支撑数以千万计的文件。

简单的一致性模型

HDFS应用需要一个“一次写入多次读取”的文件访问模型。一个文件经过创建、写入和关闭之后就不需要改变。这一假设简化了数据一致性问题,并且使高吞吐量的数据访问成为可能。Map/Reduce应用或者网络爬虫应用都非常适合这个模型。目前还有计划在将来扩充这个模型,使之支持文件的附加写操作。

“移动计算比移动数据更划算”

一个应用请求的计算,离它操作的数据越近就越高效,在数据达到海量级别的时候更是如此。因为这样就能降低网络阻塞的影响,提高系统数据的吞吐量。将计算移动到数据附近,比之将数据移动到应用所在显然更好。HDFS为应用提供了将它们自己移动到数据附近的接口。

集群均衡

HDFS的架构支持数据均衡策略。如果某个Datanode节点上的空闲空间低于特定的临界点,按照均衡策略系统就会自动地将数据从这个Datanode移动到其他空闲的Datanode。当对某个文件的请求突然增加,那么也可能启动一个计划创建该文件新的副本,并且同时重新平衡集群中的其他数据。这些均衡策略目前还没有实现。


四、通信(namenode)


五、常见问题的解决

5.1存储大量小文件

要想让HDFS能处理好小文件,有不少方法。利用SequenceFile、MapFile、Har等方式归档小文件,这个方法的原理就是把小文件归档起来管理,HBase就是基于此的。对于这种方法,如果想找回原来的小文件内容,那就必须得知道与归档文件的映射关系。横向扩展,一个Hadoop集群能管理的小文件有限,那就把几个Hadoop集群拖在一个虚拟服务器后面,形成一个大的Hadoop集群。google也是这么干过的。多Master设计,这个作用显而易见了。正在研发中的GFS II也要改为分布式多Master设计,还支持Master的Failover,而且Block大小改为1M,有意要调优处理小文件啊。附带个Alibaba DFS的设计,也是多Master设计,它把Metadata的映射存储和管理分开了,由多个Metadata存储节点和一个查询Master节点组成。

作者:张冬
链接:https://www.zhihu.com/question/26504749/answer/33012474
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

其实很简单,可以参考Google的GFS以及变种HDFS、淘宝TFS以及腾讯TencentFS的设计。这些都是处理大量小文件的典范。

大家知道传统的文件系统下,每个文件都要被创建对应的inode之类元数据,但是在海量文件场景下,传统FS已经无法承载如此多的元数据IO量以及如此庞大的元数据搜索计算量了,唯一的做法就是降低元数据量,那么势必就要降低文件实体的数量,所以这些文件系统无一例外的都是用了这样一种变通的方法,即在文件中再创建文件,比如一个64MB的大文件,比如其中可以包含16384个4KB的小文件,但是这个64MB的大文件只占用了1个inode,而如果存放4KB的文件的话,就需要16384个inode了。

那么如何寻址这个大文件中的小文件呢?方法就是利用一个旁路数据库来记录每个小文件在这个大文件中的起始位置和长度等信息,也就是说将传统文件系统的大部分元数据剥离了开来,拿到了单独的数据库中存放,这样通过查询外部数据库先找到小文件具体对应在哪个大文件中的从哪开始的多长,然后直接发起对这个大文件的对应地址段的读写操作即可。另外还可以创建索引以加速文件查找动作。

在一个海量分布式文件系统中,元数据就像上面的思想一样是分级的,中控节点,也就是MDS,存储一级元数据,也就是大文件与底层块的对应关系,而数据节点则存放二级元数据,也就是最终的用户文件在这些一级大块中的存储位置对应关系,经过两级寻址从而读写数据。其实这些一级大文件,就可以认为它们是卷了,也就是在卷管理层之上再存放文件,这样就降低了单一空间下的文件总数量从而提高性能。

常见的有fastdfs(trunk模式), TFS, weed-fs (go写的facebook haystack实现)等

上面的方案都是介绍新的系统,但是在实际的hdfs中的优化方案不可用。具体的可操作方案见https://blog.csdn.net/sunnyyoona/article/details/53870077

http://www.aboutyun.com/thread-14227-1-1.html
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值