文章目录
1. HDFS简介
HDFS(Hadoop Distributed File System)是Hadoop自带的分布式文件系统,基于Java语言实现的分布式、可横向扩展的文件系统。HDFS的设计主要是为了实现存储大量数据、成本低廉和容错率高、数据一致性,以及顺序访问数据这4个目标。
2. HDFS基本概念
2.1 数据块(Block)
HDFS默认的最基本存储单位是数据块,在HDFS 1.x中默认数据块大小为64MB,在HDFS 2.x中默认数据块大小为128MB。但是与单一的文件磁盘系统不同的是,HDFS中小于一个块大小的文件不会占用整个块的空间,文件大小是多大就占用多少存储空间。
2.2 元数据节点(NameNode)
NameNode用来管理文件系统的命名空间,将所有的文件和文件夹的元数据保存在一个文件系统树中。主要包含以下几个文件:
- VERSION:存放版本,保存版本号。
- edits: 当文件系统进行写操作时,首先把它记录在修改日志中,元数据节点在内存中保存了文件系统的元数据信息。在记录了修改日志后,元数据节点则修改内存的数据结构。每次写操作成功之前,修改日志都会同步到文件系统。
- fsimage:文件即命名空间文件。
上图为namenode对元数据的操作过程图,这种工作方式的特点:
(1)namenode始终在内存中存储元数据(metedata),使得“读操作”更加快、
(2)有“写请求”时,向edits文件写入日志,成功返回后才修改内存,并向客户端返回。
(3)fsimage文件为metedata的镜像,不会随时同步,与edits合并生成新的fsimage。
从以上特点可以知道,edits文件会在集群运行的过程中不断增多,占用更多的存储空间,虽然有合并,但是只有在namenode重启时才会进行。并且在实际工作环境很少重启namenode,
这就带来了一下问题:
(1)edits文件不断增大,如何存储和管理?
(2)因为需要合并大量的edits文件生成fsimage,导致namenode重启时间过长。
(3)一旦namenode宕机,用于恢复的fsiamge数据很旧,会造成大量数据的丢失。
2.3 数据节点(DataNode)
DataNode是文件系统中真正存储数据的地方,一个文件按大小分成若干个Block,存储到不同的节点上,Block的大小和副本数通过Client端上传文件时设置,文件上传成功后副本数可以变更,但是数据块大小不可以改变,默认情况下每个Block都有3个副本。
2.4 从元数据节点(Secondary NameNode)
为了解决在2.2 NameNode中所提出的问题,便是运行SecondaryNameNode。SNN所做的不过是在文件系统中设置一个检查点来帮助NameNode更好的工作。它不是要取代掉NameNode也不是NameNode的备份。
SNN有两个作用,一是镜像备份,二是日志与镜像的定期合并。两个过程同时进行,称为checkpoint(检查点)。
镜像备份的作用:备份fsimage(fsimage是元数据发送检查点时写入文件)。
日志与镜像的定期合并的作用:将Namenode中edits日志和fsimage合并,防止如果Namenode节点故障,namenode下次启动的时候,会把fsimage加载到内存中,edits往往很大,导致操作往往很耗时。(这也是namenode容错的一套机制)
SNN创建检查点的工作过程
- SecondaryNameNode通知NameNode准备提交edits文件,此时主节点将新的写操作数据记录到一个新的文件edits.new中。
- SecondaryNameNode通过HTTP GET方式获取NameNode的fsimage与edits文件(在SecondaryNameNode的current同级目录下可见到 temp.check-point或者previous-checkpoint目录,这些目录中存储着从namenode拷贝来的镜像文件)。
- SecondaryNameNode开始合并获取的上述两个文件,产生一个新的fsimage文件fsimage.ckpt。
- SecondaryNameNode用HTTP POST方式发送fsimage.ckpt至NameNode。
- NameNode将fsimage.ckpt与edits.new文件分别重命名为fsimage与edits,然后更新fstime,整个checkpoint过程到此结束。SecondaryNameNode备份由三个参数控制fs.checkpoint.period控制周期(以秒为单位,默认3600秒),fs.checkpoint.size控制日志文件超过多少大小时合并(以字节为单位,默认64M), dfs.http.address表示http地址,这个参数在SecondaryNameNode为单独节点时需要设置。
从工作过程可以看出,SecondaryNameNode的重要作用是定期通过编辑日志文件合并命名空间镜像,以防止编辑日志文件过大。SecondaryNameNode一般要在另一台机器上运行,因为它需要占用大量的CPU时间与namenode相同容量的内存才可以进行合并操作。它会保存合并后的命名空间镜像的副本,并在namenode发生故障时启用。
3. HDFS体系结构
3.1 HDFS架构图
3.2 HDFS体系架构概述
HDFS采用master/slave架构。一个HDFS集群是一个Namenode和一定数量的Datanode组成,NN是一个中心服务器,负责管理文件系统的名字空间和客户端对文件的访问。Datenode在集群中一般是一个节点一个,负责管理节点上他们附带的存储。从内部看,一个文件其实分成一个或多个block,这些block存储在Datanode集合里。Namenode执行文件系统的namespace操作,例如打开、关闭、重命名文件和目录,同时决定block到具体Datanode节点的映射。Datanode在Namenode的指挥下进行block的创建、删除和复制。Namenode和Datenode都是设计成可以跑在普通的廉价的运行linux的机器上。由于HDFS采用java语言开发,因此可以部署在很大范围的机器上。一个典型的部署场景是一台机器跑一个单独的NN节点,集群中的其他机器各跑一个Datanode实例。这个架构并不排除一台机器上跑多个Datanode,不过这比较少见。
3.3 架构组件功能
3.3.1 文件系统的名字空间(namespace)
HDFS支持传统的层次型文件组织结构,与大多数其他文件系统类似,用户可以创建目录,并在其间创建、删除、移动和重命名文件。HDFS不支持用户磁盘配额和访问权限控制,也不支持硬链接和软链接,不过当前的架构并不排除实现这些特性。NN维护文件系统的namespace,任何对文件系统namespace和文件属性的修改将被NN记录下来。应用程序可以设置HDFS保存的文件的副本数目,文件副本的数目称为文件的副本系数,这个信息也是由NN保存的。
3.3.2 数据复制
HDFS被设计成在一个大集群中可以跨机器地可靠地存储海量地文件。它将每个文件存储成block序列,除了最后一个block,所有的block都是同样的大小。文件的所有bolck为了容错都会被复制。每个文件的block大小和(文件的副本系数)replication因子都是可配置的。Replication因子可以在文件创建的时候配置,以后也可以改变。HDFS中的文件是write-one,并且严格要求在任何时候只有一个writer。NN全权管理block的复制,它周期性地从集群中的每个DB接收心跳包和一个Blockreport。心跳包的接收表示该DN节点正常工作,而Blockreport包括了该DN上所有的block组成的列表。
3.3.3 元数据块
元数据(Metadata)是描述其它数据的数据(data about other data),或者说是用于提供某种资源的有关信息的结构数据(structured data)。
元数据包括:
-文件系统目录树信息
-文件名,目录名
-文件和目录的从属关系
-文件和目录的大小,创建及最后访问时间
-权限
-文件和块的对应关系
-文件由哪些块组成
-块的存放位置
-机器名,块ID
3.3.4 数据块
兼容HDFS的应用都是处理大数据集合的。这些应用都是写数据一次,读却是一次到多次,并且读的速度要满足流式读。HDFS支持文件的write-once-read-many语义。一个典型的block大小是64MB,因而,文件总是按照64M切分成chunk,每个chunk存储于不同的Datanode。
在传统的块存储介质中,块是读写的最小数据单位(扇区),传统文件系统基于存储块进行操作。
HDFS也使用了块的概念,默认64MB
-可针对每个文件配置,由客户端指定
-每个块有一个自己的全局ID
HDFS将一个文件分为一个或数个块来存储
-每个块是一个独立的存储单位
-以块为单位在集群服务器上分配存储
与传统文件系统不同的是,如果实际数据没有达到块大小,则并不实际占用磁盘空间
-如果一个文件是200M,则它会被分为4个块:64+64+64+8
当一个文件大于集群中任意一个磁盘的时候,文件系统可以充分利用集群中所有的磁盘;管理块使底层的存储子系统相对简单;块更加适合备份,从而为容错和高可用性的实现带来方便;最重要的使,采用块方式,实现了名字与位置的分离,实现了存储位置的独立性。
4. HDFS读写策略
4.1 HDFS读策略
- 客户端调用FileSystem实例的open方法,获得DistributedFileSystem的一个实例。
- 通过RPC远程调用NameNode,获得NameNode中此文件对应的数据块保存位置,包括这个文件的副本的保存位置(主要是各DataNode的地址)。
- DataNode根据他们与客户端的距离来排序,如果该客户端本身就是一个DataNode,那么该客户端将会从包含有相应数据块副本的本地DataNode读取数据。DistributedFileSystem类返回一个FSDataInputStream对象给客户端并读取数据,FSDataInputStream转而封装DFSInputStream对象,该对象管理着DN和NN的I/O。
- 如果客户端和其中一个DataNode位于同一机器(比如MapReduce)过程中的mapper 和 reducer ,那么就会直接从本地读取数据。
- 到达数据块末端,DFSInputStream关闭与这个DataNode的连接,然后重新查找下一个数据块。
- 不断执行第2-5步直到数据全部读完。
- 客户端调用close,关闭输入流DFSInputStream。
4.2 HDFS写策略
- 客户端调用DistributedFileSystem实例的create方法,创建文件。NameNode通过一些检查,比如文件是否存在,客户端是否拥有创建权限等;通过检查之后,在NameNode添加文件信息。注意,因为此时文件没有数据,所以NameNode上也没有文件数据块的信息。
- 创建结束之后,HDFS会返回一个输出流,FSDataOutputStream对象给客户端,这样客户端就可以写入数据了。
- 和读策略类似,FSDataOutputStream封装一个DFSOutputStream对象,该对象会负责处理DN和NN之间的通信。
- 在客户端写入数据的时候DFSOutputStream将它分成一个个的数据包,这些分包会写入一个输出流的内部队列Data队列中。接收完数据分包,输出流DFSOutputStream会向NameNode申请保存文件和副本数据块的若干个DataNode,这若干个DataNode会形成一个数据传输管道。DFSOutputStream将数据传输给距离上最短的DataNode,这个DataNode接收到数据包之后会传给下一个DataNode。数据在各DataNode之间通过管道流动,而不是全部由输出流分发,以减少传输开销。
- 因为各DataNode位于不同机器上,数据需要通过网络发送,所以,为了保证所有DataNode的数据都是准确的,接受到数据的DataNode要向发送者发送ACK。对于某个数据块,只有当DFSOutputStream收到了所有DataNode的ACK,才能确认传输结束。DFSOutputStream内部专门维护了一个等待ACK队列,这个队列保存已经进入管道传输数据、但是并未被完全确认的数据包。
- 不断执行3-5步直到数据全部写完,客户端调用close关闭文件。
- DFSOutputStream继续等待直到所有数据写入完毕并被确认,调用complete方法通知NameNode文件写入完成。NameNode接受到complete消息之后,等待相应数量的副本写入完毕后,告知客户端。
5. HDFS的优缺点
5.1 优点
- 处理超大文件。
- 运行于廉价机器上。
- 流式地访问数据。
HDFS提供一次写入,多次读取的服务。比如你在HDFS上存储了一个要处理的问题,后续可能会有多个作业都会使用到这份数据,那么只需要通过集群来读取前面已存储好的数据即可。HDFS设计之初是不支持对文件追加内容的,后来随着Hadoop生态圈的发展,现在已经支持对已有内容的追加。
- 高容错率。
数据自动保存多个副本,副本丢失后自动恢复。
5.2 缺点
- 不适合低延迟数据访问。
实时性、低延迟的查询使用HBase会更好一些,但是HBase中的rowkey设计的是否合适也会决定你的查询性能的高低。
- 无法高效存储大量小文件。
集群中小文件过多,会导致NameNode的压力增加,进而影响集群的性能。对此我们可以采用SequenceFile等方式对小文件进行合并,或者是使用NN Federation的方式来改善。
6. 小结
到此,,,HDFS的基本内容已经告一段落,,但是还有一些进阶的技能更是不可或缺的,,所以在未来的某一时间我会抽时间写以下内容:
- HDFS 高可用机制
- HDFS 副本机制
- 数据负载均衡
- 序列化、反序列化、SequenceFile、MapFile