Hadoop是一个开发和运行处理大规模数据的软件平台,是Apache的一个用Java语言实现开源软件框架,实现在大量计算机组成的集群中对海量数据进行分布式计算。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力高速运算和存储。
Hadoop框架中最核心设计就是:HDFS和MapReduce。HDFS提供了海量数据的存储,MapReduce提供了对数据的计算。HDFS在Hadoop中扮演了非常基础的作用,以文件系统的形式为上层应用提供海量数据的存储服务。
HDFS作为一个分布式文件系统,具有高容错的特点。它可以部署在廉价的通用硬件上,提供高吞吐率(Throughput)的数据访问,特别适合那些需要处理海量数据集的应用程序。它没有遵循POSIX的要求,不支持ls,cp这样标准的UNIX命令,也不支持fopen和fread这样的文件读写方法。它采用全新的设计,提供了一套特有的,基于Hadoop抽象文件系统的API,支持以流的方式访问文件系统的数据。
1. HDFS的特征与局限
HDFS有以下特性:
- 支持超大文件。理论上,HDFS上的单个文件可以存储到整个集群的所有存储空间。
- 硬件故障的快速恢复。由于集群包含成百上千个节点,硬件故障就是一个非常常见的问题了。故障检测和自动恢复在HDFS设计之初就是很重要的设计目标。
- 流式数据访问。HDFS处理的数据规模都比较大,应用一次需要访问大量的数据。同时,这些应用一般是批量处理,而不是用户交互式处理。HDFS使得应用程序能够以stream的方式访问数据集,注重的是数据的吞吐量,而不是数据访问的速度。
- 简化的一致性模型。HDFS的文件一旦创建,只能追加,不能对已有数据进行修改。这样简单的一致性模型有利于提供高吞吐量的数据访问。
正是由于上面的一些设计特征,因此HDFS并不适合以下应用:
- 低延时数据访问。在用户交互性的应用中,应用需要在ms或者几个s的时间内得到响应。由于HDFS为高吞吐率做了设计,也因此牺牲了快速响应。对于低延时的应用,可以考虑使用HBase或者Cassandra。
- 大量的小文件。标准的HDFS数据块的大小是64M,存储小文件并不会浪费实际的存储空间,但是无疑会增加了在NameNode上的元数据,大量的小文件会影响整个集群的性能。前面我们知道,Btrfs为小文件做了优化-inline file,对于小文件有很好的空间优化和访问时间优化。
- 多用户写入,修改文件。HDFS的文件只能有一个写入者,而且写操作只能在文件结尾以追加的方式进行。它不支持多个写入者,也不支持在文件写入后,对文件的任意位置的修改。
但是在大数据领域,分析的是已经存在的数据,这些数据一旦产生就不会修改,因此,HDFS的这些特性和设计局限也就很容易理解了。HDFS为大数据领域的数据分析,提供了非常重要而且十分基础的文件存储功能。
2. 体系结构
2.1 概述
HDFS采用了主从式(Master/Slave)的体系结构,其中NameNode(NN),DataNode(DN)和Client是HDFS中的3个重要角色。HDFS也在社区的努力下不断演进,包括支持文件追加,Federation,HA的引入等。关于演进历史可以点击此处。关于HA和Federation的内容,请参考博文《HDFS HA: 高可靠性分布式存储系统解决方案的历史演进》。本文仅仅涉及Secondary NameNode(SNN)。
在一个HDFS中,有一个NN,一个SNN和众多的DN,在大型的集群中可能会有数以千计的DN。而Client,一般意义上比数据节点的个数还要多。它们之间的关系如下图:
NN管理了HDFS两个最重要的关系:
- 目录文件树结构和文件与数据块的对应关系:会持久化到物理存储中,文件名叫做fsimage。
- DN与数据块的对应关系,即数据块存储在哪些DN中:在DN启动时会上报到NN它所维护的数据块。这个是动态建立的,不会持久化。因此,集群的启动可能需要比较长的时间。
而DN则保存了数据块。并且执行NN的命令,比如复制,拷贝,删除等操作。
Client则是使用HDFS的主题,包括写文件,读文件等常见操作。
2.2 数据块
上文多次提到数据块,那么什么是数据块?为了便于管理,设备往往将存储空间组织成具有一定结构的存储单位。比如硬盘,文件以块的形式存储在硬盘中,块的大小代表了系统读和写操作的最小单位;在Linux的Ext3文件系统中,块的大小默认为4K。文件系统通过一个块大小的整数倍来使用硬盘。硬盘上的数据块管理属于文件系统实现的内部细节;对于调用系统接口来读写文件的用户来说是透明的。
HDFS的块也是类似的原理。只不过它的块拥有更大的数据单元,默认的数据块大小是64M。HDFS将文件进行分块,块作为单独的存储单元,在Linux上以普通文件的形式保存在DN的文件系统中。数据块是HDFS文件存储的基本单位。在这里要注意,HDFS并不直接管理硬盘,它是通过DN上的文件系统存储数据块,通过管理数据块,来完成文件管理服务的。
HDFS是针对大文件设计的分布式系统,使用数据块有许多的好处:
- 文件大小不受单个DN的存储空间限制,文件可以保存到任意的DN上。
- 简化了存储子系统。简单化是所有系统的设计追求。特别是在故障种类繁多的分布式系统。将管理块和管理文件分开,简化了存储管理,也消除了分布式管理文件元数据的复杂性。
- 方便容错,有利于数据复制。在HDFS中,为了应对损坏的块以及硬盘,机器故障,数据块会在不同的节点上进行复制。如果一个数据块的副本丢失或者损坏,系统会在其他地方读取副本,并且数据块会复制到一个正常运行的节点上,保证数据块副本数的最低要求。当然了,这个过程对于Client来说是透明的,它实现了分布式系统中的位置透明性和故障透明性。
那么为什么数据块那么大呢?其实和文件系统的大数据块的原理是一样的。在普通的文件系统中使用较大的数据块,可以减少管理数据块所需要的开销,如在Linux中可以减少保存在i-node中硬盘地址表中的信息链的长度;同时在对文件进行读写时,可以减少寻址开销,即硬盘定位数据块的次数。在HDFS中采用大数据库唉,可以减少NN上管理文件和数据块关系的开销,而且在文件读写时,减少因为建立网络连接的开销。
2.3 NN
NN是HDFS主从结构中主节点上运行的主要进程,它负责管理从节点DN。NN维护着整个文件系统的文件目录树,文件目录的元信息和文件的数据块索引。这些信息以两种信息保存在本文文件系统中,一种是文件系统镜像(文件名字fsimage),另一种是fsimage的编辑日志(文件名字edits)。
fsimage中保存着某一特定时刻HDFS的目录树、元信息和文件数据块的索引等信息,后续的对这些信息的改动,则保存在编辑日志中,它们一起提供了一个完整的NN的第一关系。
同时,通过NN,Client还可以了解到数据块所在的DN的信息。需要注意的是,NN中关于DN的信息是不会保存到NN的本地文件系统的,也就是上面提到的fsimage和edits中。NN每次启动时,都会通过每个DN的上报来动态的建立这些信息。这些信息也就构成了NN第二关系。
另外,NN还能通过DN获取HDFS整体运行状态的一些信息,比如系统的可用空间,已经使用的空间,各个DN的当前状态等。
2.4 SNN
Secondary NameNode是用于定期合并fsimage和edits的。与NN一样,每个集群都有一个SNN,在大规模部署的条件下,一般SNN也独自占用一台服务器。SNN按照集群配置的时间建个,不停的获取HDFS某一个时间点的fsimage和edits,合并它们得到一个新的fsimage。该fsimage上传到NN后会替换NN的老的fsimage。
从上面可以看出,SNN会配合NN,为NN的第一关系提供了一个简单的CheckPoint机制,并避免出现edits过大,导致NN启动时间过长的问题。
注意,SNN并不是NN的strandby。NN是HDFS集群中的SPOF(Single Point of Failure)。通过SNN,可以减少NN的启动时间并且降低NN元数据丢失的风险。但是,在HA出现前,NN的失效是要通过人工干预的。
2.5 DN
DN是HDFS中硬盘IO最忙碌的部分:将HDFS的数据块写到Linux本地文件系统中,或者从这些数据块中读取数据。DN作为从节点,会不断的向NN发送心跳。初始化时,每个DN将当前节点的数据块上报给NN。NN也会接收来自NN的指令,比如创建、移动或者删除本地的数据块,并且将本地的更新上报给NN。
下图是NN上文件和数据块的对应关系和DN与数据块的对应关系的示意。
图中有两个文件,data1有三个数据块,表示为b1、b2和b3。data2文件由b4和b5组成。这两个文件的内容封三到几个DN上。示意中,每个数据块都有三个副本。
2.6 Client
Client是用户和HDFS进行交互的手段,HDFS提供了各种各样的Client,包括CLI,Java API,Thrift接口,C语言库,用户空间文件系统(FUSE)等。虽然Hadoop不是一个POSIX的文件系统,不支持ls,cp这样的命令,但是Hadoop还是提供了一套和Linux文件命令类似的命令行工具,比如以下命令可以创建一个目录:
hadoop fs -mkdir testDIR
通过命令行工具,可以进行一些典型的文件操作,比如读文件,创建文件路径,移动文件,重命名,列出文件列表等。同时,命令行工具也提供了本地文件和HDFS交互的能力,可以通过以下命令,将本地文件上传到HDFS:
hadoop fs -copyFromLocal testInput/hello.txt /user/alice/in/hello.txt
命令行工具提供了访问HDFS的基本能力,而Java API则提供了进一步的功能。目前,所以访问HDFS的接口都是基于Java API的,包括上面介绍的命令行工具。下面是一个非常简单的Java API的例子。
@Test public void testUpload() throws Exception{ Configuration conf = new Configuration(); conf.addResource(new Path("D:\\myeclipse\\Hadoop\\hadoopEx\\src\\conf\\hadoop.xml")); FileSystem hdfs = FileSystem.get(conf); Path src = new Path("F:\\lzp\\T.txt"); Path dst = new Path("/"); hdfs.copyFromLocalFile(src, dst); System.out.println("Upload to " + conf.get("fs.default.name")); FileStatus files[] = hdfs.listStatus(dst); for(FileStatus file : files){ System.out.println(file.getPath()); } }
尊重原创,未经允许不得转载:www.anzhan.me
参考资料:
1. Hadoop技术内幕-深入解析HADOOP COMMON和HDFS架构设计与实现原理