Hadoop:The Definitive Guid 总结 Chapter 3 Hadoop分布式文件系统

1.HDFS的设计

HDFS设计的适合对象:超大文件(TB级别的文件)、流式数据访问(一次写入,多次读取)、商用硬件(廉价硬件)

HDFS设计不适合的对象:低时间延迟的数据访问、大量的小文件、多用户写入,任意修改文件

 

 

 

2.HDFS的概念

1).数据块(Block)

HDFS中Block的大小默认是64M,小于块大小的的文件并不占据整个块的全部空间(而是将文件大小作为块的大小.比如要存放的文件是1k,但是系统的Block默认是64MB,存放之后块的大小是1k,不是64MB.文件若是大于64MB,则分多快进行存储.)

使用Blocks的好处:

  • 可以存储大文件,一个文件的大小可以大于网络中任意一个单块硬盘的容量
  • 把存储单元抽象成块而不是文件,大大简化了存储子系统的设计:简化了数据管理、取消元数据关注
  • 能很好适应数据复制,数据复制保证系统的容错和可用性。

 

2)NameNode和DataNode

HDFS  提供了两类节点

  • NameNode:管理者,负责管理文件系统的命名空间,维护着整个文件系统树树内的文件和目录,同时记录着每个文件各个块所在的数据节点信息,所有信息都以保存在本地磁盘:命名空间镜像文件(namespace image)和编辑日志文件(edit log)。
  • DataNode:工作者,根据需要存储并检索数据块(受客户端或者NameNode调度),并定期向NameNode发送他们所存储的块列表

另外Client端代表用户通过NameNode和DataNode交互来访问整个文件系统

没有NameNode,文件系统将无法使用,所以提供两种对NameNode实现容错机制:

  • 备份存储持久化状态的文件系统元数据的文件
  • 提供secondary namenode。Secondary的主要角色是定期合并命名空间镜像文件和编辑日志文件,防止edit log过大。但是secondary namenode的数据较master namenode的数据有所延迟,所有数据恢复以后肯定会有数据丢失

 

3).HDFS Federation

  • federation使用了多个独立的namenode/namespace,这样就可以水平扩展namenode,这些namenode之间是联合的,也就是说,他们之间相互独立且不需要互相协调,各自分工,管理自己的区域。
  • 分布式的datanode被用作通用的数据块存储存储设备。每个datanode要向集群中所有的namenode注册,且周期性地向所有namenode发送心跳和块报告,并执行来自所有namenode的命令。
  • 一个block pool由属于同一个namespace的数据块组成,每个datanode可能会存储集群中所有block pool的数据块。每个block pool内部自治,也就是说各自管理各自的block,block pool之间不能交流。一个namenode挂掉了,不会影响其他namenode。
  • 某个namenode上的namespace和它对应的block pool一起被称为namespace volume。它是管理的基本单位。当一个namenode/nodespace被删除后,其所有datanode上对应的block pool也会被删除。当集群升级时,每个namespace volume作为一个基本单元进行升级。

如下如所示:

 

 

4).Hadoop高可用性

Hadoop2.0.0版式本中,提供了一种机制可以使处于备用状态的Namenode中的数据与处于活动状态的Namenode中的数据同步,这种机制的实现必须需要这两个NameNode可访问在一个共享存储设备(比如:来自NAS上的NFS)上的目录。

1).当namespace被处于活动状态的NameNode修改时,这个修改操作被持久化的写入到共享目录里的一个编辑日志文件里,处于备用状态的NameNode不断的查看这个共享目录中的编辑日志文件,发现这个编辑日志文件有变化,就把它们拷贝到自己的namespace里。

2).活动状态的NameNode崩溃时,备用状态的NameNode代替崩溃的NameNode成为处于活动状态的NameNdoe,而在此之前处于备用状态的NameNode会确保它从共享目录中全部读取了编辑日志中的记录,这样就确保了在失效备援以前这两个NameNode中的namespace是完全同步的。

3).为提供快速的失效备援, 需要处于备份状态的NameNode结点有集群中块位置的最新信息,为了实现这一点,处于这两个NameNode管理的所有DataNodes,都需要向这两个NameNode发送块信息和心跳信息

4).对正确操作高可用性的集群而言,至关重要的一点,是在任何时刻这两个NameNode只参有一个NameNode处于活动状态,否则namespace将会处于不一致状态,这将会导致数据丢失或其他不可知结果。

 

 

 

3.命令行接口

基本命令:

1).将本地数据拷贝:

  • % hadoop  fs -copyFromLocal input/docs/quangle.txt   hdfs://localhost/user/tom/quangle.tx
  • % hadoop fs -copyFromLocal   input/docs/quangle.txt /user/tom/quangle.txt              ---这里面命令可以省去hdfs://
  • % hadoop fs -copyFromLocal   input/docs/quangle.txt quangle.txt                              ---使用了相对路径

2).将数据从hdfs上拷贝到本地硬盘并检查文件时候一致

  • % hadoop fs -copyToLocal quangle.txt  quangle.copy.txt
  • % md5 input/docs/quangle.txt  quangle.copy.tx

3).HDFS文件列表

% hadoop fs -mkdir books

% hadoop fs -ls .

Found 2 items

drwxr-xr-x  -   tom  supergroup  0      2009-04-02   22:41   /user/tom/books

-rw-r--r--   1   tom  supergroup  118    2009-04-02   22:29   /user/tom/quangle.txt

结果当中各列分别表示:

文件模式、文件被复制的份数、文件拥有者、文件拥有者的group、文件大小,目录显示为0、、文件最后修改日期、文件最后修改时间、文件的绝对路径

 

 

 

4.Hadoop文件系统

Hadoop有一个对文件系统的抽象,HDFS只是其中的一个实现。Java的抽象类org.apache.hadoop.fs.FileSystem代表了Hadoop中的文件系统,还有其他的几种实现(P48)

接口:

Hadoop用Java写成,所有Hadoop文件的交互都通过Java api来完成。还有另外的与Hadoop文件系统交互的库:Thrift、C、FUSE、WebDAV等

 

 

 

5.Java接口

1).从Hadoop URL中读取数据

使用java.net.URL对象打开数据流,进而从中读取数据:

InputStream in = null;
try {
    in = new URL("hdfs://host/path").openStream();
    // process in
    } finally {
    IOUtils.closeStream(in);
    }
}

 

代码示例:

下面展示了程序以标准输出方式显示Hadoop文件系统中的文件,这里采用的方法通过FsUrlStreamHandlerFactory实例调用URL中的setURLStreamHandlerFactory方法,这个操作对一个jvm只能使用一次,我们可以在静态块中调用。

public class URLCat {
    static {
        URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
    }

    public static void main(String[] args) throws Exception {
        InputStream in = null;
        try {
            in = new URL(args[0]).openStream();
            IOUtils.copyBytes(in, System.out, 4096, false);
        } finally {
            IOUtils.closeStream(in);
        }
    }
}

 

2).通过FileSystem API读取数据

FileSystem是一个通用文件系统API,其中其对象的open方法返回的是FSDataInputStream对象,此对象支持随机访问。

Configuration对象封装了客户端或服务器的配置,通过设置配置文件读取类路径来实现

public class FileSystemCat {
    public static void main(String[] args) throws Exception {
        String uri = args[0];
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(URI.create(uri), conf);
        InputStream in = null;
        try {
            in = fs.open(new Path(uri));
            IOUtils.copyBytes(in, System.out, 4096, false);
        } finally {
            IOUtils.closeStream(in);
        }
    }
}

运行结果:

% hadoop FileSystemCat hdfs://localhost/user/tom/quangle.txt

On the top of the Crumpetty Tree
The Quangle Wangle sat,
But his face you could not see,
On account of his Beaver Hat.

package org.apache.hadoop.fs;

public class FSDataInputStream extends DataInputStream implements Seekable,
        PositionedReadable {
    // implementation elided
}

 

Seekable接口支持在文件中找到指定位置,其中seek()可以移到文件中任意一个绝对位置,skip()则只能相对于当前位置定位到另一个新位置,seek()是一个方法是一个相对高开销的操作,需要慎重使用。

public interface Seekable {
    void seek(long pos) throws IOException;

    long getPos() throws IOException;
}

 

public class FileSystemDoubleCat {
    public static void main(String[] args) throws Exception {
        String uri = args[0];
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(URI.create(uri), conf);
        FSDataInputStream in = null;
        try {
            in = fs.open(new Path(uri));
            IOUtils.copyBytes(in, System.out, 4096, false);
            in.seek(0); // go back to the start of the file
            IOUtils.copyBytes(in, System.out, 4096, false);
        } finally {
            IOUtils.closeStream(in);
        }
    }
}

运行结果:

% hadoop FileSystemDoubleCat hdfs://localhost/user/tom/quangle.txt

On the top of the Crumpetty Tree
The Quangle Wangle sat,
But his face you could not see,
On account of his Beaver Hat.
On the top of the Crumpetty Tree
The Quangle Wangle sat,
But his face you could not see,
On account of his Beaver Hat.

 

在下面代码中,read()方法最多读取length bytes。Position是相对offset的偏移,buffer存放读取的数据。readFully()方法读取length bytes的数据到buffer中,第二个readFully则是读取buffer.length bytes的数据到buffer中。以下的方法均不会改变offset的值。

public interface PositionedReadable {
    public int read(long position, byte[] buffer, int offset, int length)
            throws IOException;

    public void readFully(long position, byte[] buffer, int offset, int length)
            throws IOException;

    public void readFully(long position, byte[] buffer) throws IOException;
}

 

3).写入数据

FileSystem类创建文件的方法create 参数为指定的一个Path对象

public FSDataOutputStream create(Path f) throws IOException;


重要方法Progressable,可以把数据写入数据节点的进度通知应用

package org.apache.hadoop.util;

public interface Progressable {
    public void progress();
}

 

新建文件方法append(),在一个已有的文件末尾追加数据

public FSDataOutputStream append(Path f) throws IOException;

 

程序实例:将本地文件复制到Hadoop文件系统

public class FileCopyWithProgress {
    public static void main(String[] args) throws Exception {
        String localSrc = args[0];
        String dst = args[1];
        InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(URI.create(dst), conf);
        OutputStream out = fs.create(new Path(dst), new Progressable() {
            public void progress() {
                System.out.print(".");
            }
        });
        IOUtils.copyBytes(in, out, 4096, true);
    }
}

执行结果:

% hadoop FileCopyWithProgress input/docs/1400-8.txt hdfs://localhost/user/tom/
1400-8.txt
...............


create()方法返回的FSDataOutputStream对象

package org.apache.hadoop.fs;

public class FSDataOutputStream extends DataOutputStream implements Syncable {
    public long getPos() throws IOException {
        // implementation elided
    }
    // implementation elided
}

 

 

4).目录

创建目录方法mkdir()方法,如果目录创建成功返回true

public boolean mkdirs(Path f) throws IOException;


 

5).查询文件系统

文件元数据:FileStatus,FileSystem的getFilesStatus()方法用于获取文件或目录的FileStatus对象

示例代码:

public class ShowFileStatusTest {
...........
    public void fileStatusForFile() throws IOException {
        Path file = new Path("/dir/file");
        FileStatus stat = fs.getFileStatus(file);
        assertThat(stat.getPath().toUri().getPath(), is("/dir/file"));
        assertThat(stat.isDir(), is(false));
        assertThat(stat.getLen(), is(7L));
        assertThat(stat.getModificationTime(),
                is(lessThanOrEqualTo(System.currentTimeMillis())));
        assertThat(stat.getReplication(), is((short) 1));
        assertThat(stat.getBlockSize(), is(64 * 1024 * 1024L));
        assertThat(stat.getOwner(), is("tom"));
        assertThat(stat.getGroup(), is("supergroup"));
        assertThat(stat.getPermission().toString(), is("rw-r--r--"));
    }
..........
}


列出文件,下列listStatus函数能够列出目录的内容能个

public FileStatus[] listStatus(Path f) throws IOException;
public FileStatus[] listStatus(Path f, PathFilter filter) throws IOException;
public FileStatus[] listStatus(Path[] files) throws IOException;
public FileStatus[] listStatus(Path[] files, PathFilter filter) throws IOException;

 

public class ListStatus {
    public static void main(String[] args) throws Exception {
        String uri = args[0];
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(URI.create(uri), conf);
        Path[] paths = new Path[args.length];
        for (int i = 0; i < paths.length; i++) {
            paths[i] = new Path(args[i]);
        }
        FileStatus[] status = fs.listStatus(paths);
        Path[] listedPaths = FileUtil.stat2Paths(status);
        for (Path p : listedPaths) {
            System.out.println(p);
        }
    }
}

执行结果:

 % hadoop ListStatus hdfs://localhost/ hdfs://localhost/user/tom
hdfs://localhost/user
hdfs://localhost/user/tom/books
hdfs://localhost/user/tom/quangle.txt

 

文件模式,为了处理一批文件,Hadoop提供了"通配操作",并提供了globStatus()方法,其返回与路径相匹配的所有文件的FileStatus对象数组,并按照路径排序

public FileStatus[] globStatus(Path pathPattern) throws IOException;
public FileStatus[] globStatus(Path pathPattern, PathFilter filter) throws OException;

下面列出通配及其含义:

 

PathFilter对象,为了弥补通配符不够准确的功能,Hadoop的FileSystem在listStatus()和globStatus()提供了可选的PathFilter对象,使我们能够通过编程方式控制通配符

package org.apache.hadoop.fs;

public interface PathFilter {
    boolean accept(Path path);
}

程序实例:用于排除匹配正则表达式路径的PathFilter

public class RegexExcludePathFilter implements PathFilter {
    private final String regex;

    public RegexExcludePathFilter(String regex) {
        this.regex = regex;
    }

    public boolean accept(Path path) {
        return !path.toString().matches(regex);
    }
}

过滤方法调用:

fs.globStatus(new Path("/2007/*/*"), new RegexExcludeFilter("^.*/2007/12/31$"));

 

 

6).删除数据

使用FileSystem的delete()方法可以永久删除文件或目录,其中如果f是一个文件或者空目录recursive的值就会被忽略。当一个目录不为空的时候:recursive为true时,目录将连同内部的内容都会被删除,否则抛出IOException异常。

public boolean delete(Path f, boolean recursive) throws IOException;

 

 

 

6.数据流

1).文件读取剖析

下图显示了在读取文件时一些事件的主要顺序:

步骤如下

  1. 客户端通过调用FileSystem对象的open()方法打开需要读取的文件,对HDFS来说是调用分布式系统的一个实例DistributedFileSystem
  2. DistributedFileSystem通过RPC调用namenode确定文件的前几个block的位置。对于每一个block,namenode返回一含有那个block拷贝的datanode地址;接下来,datanode按照距离client的距离进行排序(确定距离的方法后面有介绍)。如果client本身就是一个datanode,那么就从本地datanode节点上读取数据。
  3. DistributedFileSystem返回一个FSDataInputStream给客户端,让他从FSDataInputStream中读取数据。FSDataInputStream接着包装一个DFSInputStream,他用来管理datanode和namenode的I/O
  4. client调用流的read()方法。
  5. DFSInputStream开始的时候存放了前几个blocks的datanode的地址,这时候开始连接到最近datanode上。客户端反复调用read()方法,以流式方式从datanode读取数据。
  6. 当读到block的结尾的时候,DFSInputStream会关闭到当前datanode的链接,然后查找下一个block的最好的datanode。这些操作对客户端都是透明的,客户端感觉到的是连续的流。(读取的时候就开始查找下一个块所在的地址)
  7. 读取完成之后关闭FSDataInputStream

 

关于容错处理问题:

在读取期间,如果client与datanode通信的时候如果发生错误的话,它会尝试读取下个紧接着的含有那个block的datanode。Client会记住发生错误datanode,这样它就不必在读取以后的块的时候再尝试这个datanode了。Client也验证从datanode传递过来的数据的checksum。如果错误的block被发现,它将在尝试从另一个datanode读取数据前把这个信息报告给namenode

这个设计的一个重要方面是:客户端联系datanodes直接接收数据,并且客户端被namenode导向包含每块数据的最佳datanode。这样的设计可以使HDFS扩展而适应大量的客户端,因为数据传输线路是通过集群中的所有datanode的,namenode只需要相应块的位置查询服务即可(而namenode是将块的位置信息存放在内存中的,这样效率就非常高),namenode不需要提供数据服务,因为数据服务随着客户端的增加将很快成为瓶颈。

 

关于网络拓扑与Hadoop

Hadoop计算路径是按照如下方式进行的:

  • 把网络看成树结构
  • 两个节点之间的距离=第一个节点到两个节点共同祖先节点的距离+第二个节点到两个节点共同祖先节点的距离

下面是一个例子:

distance(/d1/r1/n1, /d1/r1/n1) = 0 (processes on the same node)

distance(/d1/r1/n1, /d1/r1/n2) = 2 (different nodes on the same rack)

distance(/d1/r1/n1, /d1/r2/n3) = 4 (nodes on different racks in the same data center)

distance(/d1/r1/n1, /d2/r3/n4) = 6 (nodes in different data centers)

 

2).文件写入剖析

下图显示了在写入文件时一些事件的主要顺序:

  1. Client通过调用DistributedFileSystem的create()函数来请求创建文件(步骤1)
  2. DistributedFileSystem通过对namenode发出rpc请求,在namenode的namespace里面创建一个新的文件,但是这时候并不关联任何的块(步骤2)。Namenode进行很多检查来保证不存在要创建的文件已经存在于文件系统中,还有检查是否有相应的权限来创建文件。如果这些检查都完成了,那么namenode将记录下来这个新文件的信息,否则文件创建失败,并且客户端会收到一个IOExpection。DistributedFileSystem返回一个FSDataOutputStream给客户端用来写入数据。和读的情形一样,FSDataOutputStream将包装一个DFSOutputStream用于和datanode及namenode通信。
  3. 客户端开始写数据(步骤3)。DFSDataOutputStream把要写入的数据分成包(packet),并将它们写入到中间队列(data queue)中。Data queue中的数据由DataStreamer来读取。DataStreamer的职责是让namenode分配新的块——通过找出合适的datanodes——来存储作为备份而复制的数据。这些datanodes组成提个流水线,我们假设这个流水线是个三级流水线,那么里面将含有三个节点。DataStreamer将数据首先写入到流水线中的第一个节点,
  4. 然后由第一个节点将数据包传送并写入到第二个节点,然后第二个将数据包传送并写入到第三个节点(步骤4,5)。
  5. DFSOutputStream维护了一个内部关于packets的队列,里面存放等待被datanode确认无误的packets的信息。这个队列称为等待队列(ack queue)。一个packet的信息被移出本队列当且仅当packet被流水线中的所有节点都确认无误
  6. 当完成数据写入之后客户端调用流的close方法(步骤6),在通知namenode完成写入之前,这个方法将flush残留的packets,并等待确认信息(acknowledgement)。namenode已经知道文件由哪些块组成(通过DataStream询问数据块的分配),所以它在返回成功前只需要等待数据块进行最小值复制(步骤7)。

 

关于写入数据的时候datanode发生错误的处理

发现错误之后,首先关闭流水线,然后将没有被确认的数据放到数据队列的开头,当前的块被赋予一个新的标识,这信息将发给namenode,以便在损坏的数据节点恢复之后删除这个没有被完成的块。然后从流水线中移除损坏的datanode。之后将这个块剩下的数据写入到剩下的两个节点中。Namenode注意到这个块的信息还没有被复制完成,他就在其他一个datanode上安排复制。接下来的block写入操作就和往常一样了。

尽管可能在写入数据的时候多个节点都出现故障,但是只要默认的一个节点(dfs.replication.min)被写入了,那么这个操作就会完成。因为数据块将会在集群间复制,直到复制完定义好的次数(dfs.replication,默认3份)

 

关于副本的布局

  • 第一份存放在客户端(如果客户端没在集群上,那么这个节点将被随机选择,尽管这样,系统也不会选择磁盘容量快满的,或者是比较忙的节点)
  • 第二份存放在与第一份不同机架(Rack)的一个随机节点中
  • 第三份存放在与第二份相同的机架中,但是不在同一个节点
  • 接下来的就存放在集群中(例如DataCenter)的随机节点中了,系统尽量避免在一个机架中存放多份备份文件。

如下图:

 

3).一致模型

文件系统的一致性模型描述了读写文件过程中的数据可见性。HDFS去掉了一些POSIX对性能的要求,所以一些操作可能与你的预想不大一致

A.在文件被创建之后,希望它在文件系统的名字空间中是可见的

Path p = new Path("p");
fs.create(p);
assertThat(fs.exists(p), is(true));

 


B.但是任何没写入到文件的内容不保证可见,尽管你可能去flush流。所以文件看起来长度为0

Path p = new Path("p");
OutputStream out = fs.create(p);
out.write("content".getBytes("UTF-8"));
out.flush();
assertThat(fs.getFileStatus(p).getLen(), is(0L));

 

C.当超过一个block的数据被写入之后,第一个blockreader将是可见的,接下来的也是一样:当前正在写的block总是不可见的,已经被写入的block是可见的

 

D.HDFS通过FSDataOutputStreamsync()方法提供了一种强制使所有buffer同步到datanode方法。当sync()成功返回之后,HDFS保证sync之前的数据被持久化并且对所有reader可见。下面操作有点像unix系统的fsync系统调用,该调用提供一个文件描述符的缓冲数据。

Path p = new Path("p");
FSDataOutputStream out = fs.create(p);
out.write("content".getBytes("UTF-8"));
out.flush();
out.sync();
assertThat(fs.getFileStatus(p).getLen(), is(((long) "content".length())));

 

同步之后看到具体文件内容:

FileOutputStream out = new FileOutputStream(localFile);
out.write("content".getBytes("UTF-8"));
out.flush(); // flush to operating system
out.getFD().sync(); // sync to disk
assertThat(localFile.length(), is(((long) "content".length())));

 

E.在HDFS文件关闭文件还隐藏着执行sync()方法

Path p = new Path("p");
OutputStream out = fs.create(p);
out.write("content".getBytes("UTF-8"));
out.close();
assertThat(fs.getFileStatus(p).getLen(), is(((long) "content".length())));

 

 

7.通过distcp并行复制

Hadoop提供了一个非常有用的工具——distcp,来在Hadoop文件系统之间拷贝大量数据。

1).distcp的一个典型用途就是在两个HDFS集群之间传递数据。如果两个集群运行着相同版本的Hadoop,就非常适合使用hdfs方案:

% hadoop distcp hdfs://namenode1/foo hdfs://namenode2/bar

 

2).使用-overwrite或者-update选项改变了以前源路径和目标路径的使用方式

% hadoop distcp -update hdfs://namenode1/foo hdfs://namenode2/bar/foo

 

3).如果想在运行不同版本的HDFS集群之间拷贝使用HDFS协议运行distcp的话会产生错误。因为不同系统的RPC系统不兼容。为了补救,可以使用基于HTTP协议的HFTP从源文件中读取数据。但job就必须在拷贝的目标机器上运行,以便HDFS的rpc版本兼容。上面的例子可以写成下面的样子:

% hadoop distcp hftp://namenode1:50070/foo hdfs://namenode2/bar

注意:必须在uri中指定namenod的web端口号。这个端口的默认值是50070,由dfs.http.address属性值来决定。

 

4).保持HDFS集群的均衡

可以使用均衡器工具---balance命令

 

 

8.Hadoop存档

为了解决Hadoop存储小文件低效问题,Hadoop提供了Hadoop ArchiveHAR)文件打包工具

HAR是使用archive工具打包一些文件创建的。Archive工具运行一个MapReduce job来并行处理输入文件。所以需要在一个运行MapReduce的集群上使用它。

% hadoop fs -lsr /my/files

-rw-r--r-- 1 tom supergroup 1 2009-04-09 19:13 /my/files/a

drwxr-xr-x - tom supergroup 0 2009-04-09 19:13 /my/files/dir

-rw-r--r-- 1 tom supergroup 1 2009-04-09 19:13 /my/files/dir/b

 

运行archive命令:

% hadoop archive -archiveName files.har /my/files/m

 

运行上述命令之后,产生的.har文件信息如下:

 % hadoop fs -ls /my

Found 2 items

drwxr-xr-x - tom supergroup 0 2009-04-09 19:13 /my/files

drwxr-xr-x - tom supergroup 0 2009-04-09 19:13 /my/files.har

 

% hadoop fs -ls /my/files.har

Found 3 items

-rw-r--r-- 10 tom supergroup 165 2009-04-09 19:13 /my/files.har/_index

-rw-r--r-- 10 tom supergroup 23 2009-04-09 19:13 /my/files.har/_masterindex

-rw-r--r-- 1 tom supergroup 2 2009-04-09 19:13 /my/files.har/part-0

 

上面的结果显示了HAR文件的组成部分:两个索引文件以及部分文件的集合,对本例来说part文件只有一个。Part文件包含了原始文件的内容,index用来索引这些数据。

下面的命令以递归的方式列出了存档文件中的部分文件

hadoop fs -lsr har:///my/files.har

drw-r--r-- - tom supergroup 0 2009-04-09 19:13 /my/files.har/my

drw-r--r-- - tom supergroup 0 2009-04-09 19:13 /my/files.har/my/files

-rw-r--r-- 10 tom supergroup 1 2009-04-09 19:13 /my/files.har/my/files/a

drw-r--r-- - tom supergroup 0 2009-04-09 19:13 /my/files.har/my/files/dir

 

以下两个指令相同

% hadoop fs -lsr har:///my/files.har/my/files/dir
% hadoop fs -lsr har://hdfs-localhost:8020/my/files.har/my/files/dir

删除HAR文件指令

 % hadoop fs -rmr /my/files.har

 

HAR的不足:

1).创建的是归档文件,没有压缩功能,所以不会节省空间

2).归档文件创建之后不能被修改,若要添加、删除文件的话,需要重新建立归档文件

3).虽然HAR文件可以作为MapReduce的输入,但是InputFormat不支持将多个文件打包到一个MapReduce split中。所以处理大量的小文件,即使是在har文件中,都将是低效的。

 

 

 

posted on 2012-08-09 16:24  as_  阅读(1303)  评论(1编辑  收藏  举报

导航