Hadoop分布式文件系统
管理着跨计算机网络存储的文件系统称为分布式文件系统。使这个文件系统能容忍节点故障而不损失数据就是一个极大的挑战。
HDFS是Hadoop的旗舰级文件系统。
HDFS是为以流式数据访问模式存储超大文件而设计的文件系统。
流式数据访问:一次写入,多次读取模式是最高效的。每次分析至少会涉及数据集中的大部分数据,因此读取整个数据集的时间比读取第一条记录的延迟更为重要。
1 HDFS的概念
HDFS的块默认为64M,HDFS中小于一个块大小的文件an不会占据整个块的空间。之所以要让HDFS的块远大与磁盘块,目的是为了减小寻址开销。
MapReduce过程中国的Map任务通常是在一个时间内运行操作一个块,因此如果任务数过于少,作业的运行速度显然就比预期的慢。
抽象块的好处:一个文件可以大于网络中任意一个磁盘的容量;使用抽象单元而不是文件会简化存储子系统;块很适合为提供容错和实用性而作的复制操作。
HDFS中fsck指令会显示块的信息。
HDFS集群有两种节点,以管理者-工作者模式运行,即一个名称节点(管理者)和多个数据节点(工作者)。名称节点管理文件系统的命名空间,它维护着这个文件系统树及这个树内所有的文件和索引目录。这些信息以两种形式将文件永久保存在本地磁盘上:命名空间镜像和编辑日志。数据节点是文件系统的工作者,它们存储并提供定位块的服务,并且定时的向名称节点发送它们存储的块的列表。
确保名称节点安全的机制:1,复制那些组成文件系统元数据持久状态的文件;2,运行一个二级名称节点。
两个需要注意的属性:fs.default.name, dfs.replication(=1. HDFS就不会按默认设置将文件系统块复制3份)
Hadoop提供了许多文件系统的接口,它一般使用URI方案来选取合适的文件系统实例交互。
接口:Thrift,C语言库,FUSE(用户空间文件系统,允许一些文件系统整合为一个UNIX文件系统在用户空间中执行)
2 Java接口
从Hadoop URL中读取数据:要从Hadoop文件系统中读取文件,一个最简单的方法是使用java.net.URL对象来打开一个数据流,从而从中读取数据。
使用FileSystemAPI读取数据:有时不能在应用中设置URLStreamHandlerFactory,这时,我们需要用FIleSystem API来打开一个文件的输入流。
FSDataInputStream:FIleSystem中的open()方法实际上返回的是一个FSDataInputStream,而不是标准的java.io类。这个类是java.io.DataInputStream的一个子类,支持随机访问,这样就可以从流的任意位置读取数据了。
开发人员一般不常用seekToNewSource()方法,此方法一般倾向于切换到数据的另一个副本并在新的副本中寻找targetPos指定的位置。HDFS内部就采用这样的方法在数据节点故障时为客户端提供可靠的数据输入流。
seek()是一个相对高开销的操作,需要慎重使用。我们需要依靠流数据构建应用访问模式,而不要大量执行seek操作。
FileSystem类有一系列创建文件的方法。
create()方法需要写入的文件而创建的父目录可能原先并不存在,虽然这样很方便,但有时并不希望这样。如果我们想在父目录不存在时不执行写入,就必须在调用exits()首先检查父目录是否存在。
还有一个用于传递回调接口的重载方法progressable,这样一来,我们所写的应用就会被告知数据写入数据节点的进度。
新建文件的另一种方法是使用append()在一个已有文件中追加
create()方法返回一个FSDataOutputStream,与FSDataInputStream不同,FSDataOutputStream不允许定位,这是以为HDFS只允许对一个打开的文件顺序写入,或向一个已有文件添加。换句话说,它不支持除文件尾部的其他位置的写入,这样一来,写入时的定位就没有什么意义了。
文件元数据FIlestatus:任何文件系统的一个重要特征就是定位其目录结构及检索其存储的文件盒目录信息的能力。FIleStatus类封装了文件系统中文件和目录的元数据,包括文件长度,块大小,副本,修改时间,所有者以及许可信息。
FileSystem的getFIleStatus()方法提供了获取一个文件或目录的状态对象的方法。
listStatus()方法:列出目录的内容。传入参数是一个文件时,它会简单地返回长度为1的FileStatus对象的一个数组,当传入参数是一个目录时,它会返回0或者多个FIleStatus对象,代表着此目录所包含的文件和目录。
重载方法允许我们使用PathFilter来限制匹配的文件和目录。
文件格式:Hadoop有一个通配的操作,可以方便地使用通配符在一个表达式中核对多个文件,不需要列举每个文件和目录来指定输入。Hadoop为执行通配提供了两个FileSystem方法。
删除数据:使用FileSystem中的delete()可以永久删除文件或目录。
3 数据流
客户端通过调用FIleSystem对象的open()来读取希望打开的文件,对于HDFS来说,这个对象是分布式我那件系统的一个实例。DistributedFilesystem通过使用RPC来调用名称节点,以确定文件开头部分的块的位置。对于每一个块,名称节点返回具有该块副本的数据节点地址。此外,这些数据节点根据它们与客户端的距离来排序。如果该客户端本身就是一个数据节点(比如在一个MapReduce任务中),便从本地数据节点中读取。
这个设计的重点是,客户端直接联系数据节点去检索数据,并被名称节点指引到每个块中最好的数据节点。因为数据流动在此集群中是在所有数据节点分散进行的吗,所以这种设计能使HDFS可扩展到最大的并发客户端数量。同时,名称节点只不过是提供块位置请求(存储在内存中,因而非常高效),不是提供数据。
文件写入剖析——HDFS的连贯模型
在客户端写入数据时,DFSoutPutstream将它分成一个个的包,写入内部的队列,成为数据队列,数据队列随数据流流动,数据流的责任是根据适合的数据节点的列表离开要求这些节点为副本分配新的块。这些数据节点的列表形成一个管线——我们假设这个副本数是3,所以有三个节点在管线中。数据流将包分流给管线中的第一个的数据节点,这个节点会存储包并且发送给管线中的第二个数据节点。同样,第二个数据节点存储包并且传给管线中第三个数据节点。
DFSoutputStream也有一个内部的包队列来等待数据节点收到确认,成为确认队列。一个包只有被管线中所有节点确认后才会被移出确认队列。
一致性模型:文件系统的一致性模型描述了对文件读写的数据可见性,HDFS为性能牺牲了一些POSIX请求。
在创建一个文件之后,在文件系统的命名空间中是课件的,但是,写入文件的内容并不保证能被看见,即使数据流已经被刷新。所以文件长度显示0.一旦写入的数据超过一个块的数据,新的读取者就能看见第一个块。对于之后的块也是这样。总之,对于当前正在被写入的块,其他读取者是看不见的。HDFS提供一个方法来强制所有的缓存与数据节点同步,即在文件系统数据输出流使用sync()方法,在sync()返回成功后,HDFS能保证文件中直至写入的最后的数据对所有新的读取者而言,都是可见且一致的。此行为类似于Unix中的fsync系统调用——为一个文件描述符提交缓冲数据。
4 通过distcp进行并行复制
Hadoop有一个叫distcp(分布式复制)的有用程序,能从Hadop的文件系统并行复制大量数据。
distcp一般用于在两个HDFS集群中传输数据。distcp是作为一个MapReduce作业执行,复制工作由集群中并行运行的map来完成。这里并没有reducer。每个文件都由一个单一的map进行复制,并且distcp通过将文件分成大致相等的文件来为每个map数量大致相同的数据。
map默认的最大数量是每个集群节点(tasktracker)有20 个。
保持HDFS集群的平衡:想HDFS复制数据时,考虑集群的平衡相当重要。可以使用balancer工具继续改善集群中块的分布。
5 Hadoop归档文件
每个文件以块方式存储,块的元数据存储在名称节点的内存里,此时存储一些小的文件,HDFS会较低效。
Hadoop Archives或HAR文件,是一个更高效的将文件放入HDFS块中的文件存档设备,在减少名称节点内存使用的同时,仍然允许对文件进行透明的访问。具体地说,Hadoop Archives可以被用作MapReduce的输入。通过查看权限和路径及.har扩展名的组成,HAR文件系统将har URI转换成为一个基础文件系统的URI。
不足:创建一个归档文件会创建原始文件的一个副本,因此需要与要归档的文件同样大小的磁盘空间。
HAR文件在处理许多小文件也仍然低效。怎么样处理许多小文件的情形呢?