代码之家  ›  专栏  ›  技术社区  ›  snooze92

Hadoop文件系统中列出API调用的通配符

  •  19
  • snooze92  · 技术社区  · 12 年前

    tl;dr: 为了能够在列出的路径中使用通配符(globs),只需使用 globStatus(...) 而不是 listStatus(...) .


    上下文

    HDFS集群上的文件按分区组织,日期为 “根” 隔断文件结构的简化示例如下:

    /schemas_folder
    ├── date=20140101
    │   ├── A-schema.avsc
    │   ├── B-schema.avsc
    ├── date=20140102
    │   ├── A-schema.avsc
    │   ├── B-schema.avsc
    │   ├── C-schema.avsc
    └── date=20140103
        ├── B-schema.avsc
        └── C-schema.avsc
    

    在我的情况下,目录存储 Avro 不同类型数据的模式 (本例中为A、B和C) 在不同的日期。架构可能会开始现有、发展并停止现有。。。随着时间的流逝。


    球门

    我需要能够尽快获取给定类型的所有模式。在我希望获取类型A的所有模式的示例中,我希望执行以下操作:

    hdfs dfs -ls /schemas_folder/date=*/A-schema.avsc
    

    那会给我

    Found 1 items
    -rw-r--r--   3 user group 1234 2014-01-01 12:34 /schemas_folder/date=20140101/A-schema.avsc
    Found 1 items
    -rw-r--r--   3 user group 2345 2014-01-02 23:45 /schemas_folder/date=20140102/A-schema.avsc
    

    问题

    我不想使用shell命令,而且在JavaAPI中似乎找不到与上述命令等效的命令。当我尝试自己实现循环时,我得到了糟糕的性能。我想要 至少 命令行的性能(周围 3秒 在我的情况下)。。。


    到目前为止我发现了什么

    可以注意到它打印了两次 Found 1 items ,每个结果前一次。它不打印 Found 2 items 开始时一次。这可能意味着未在 FileSystem 但在某种程度上由客户处理。我似乎找不到合适的源代码来查看它是如何实现的。

    以下是我的第一张照片,可能有点太天真了。。。

    使用listFiles(…)

    代码:

    RemoteIterator<LocatedFileStatus> files = filesystem.listFiles(new Path("/schemas_folder"), true);
    Pattern pattern = Pattern.compile("^.*/date=[0-9]{8}/A-schema\\.avsc$");
    while (files.hasNext()) {
        Path path = files.next().getPath();
        if (pattern.matcher(path.toString()).matches())
        {
            System.out.println(path);
        }
    }
    

    结果:

    这完全符合我的预期,但由于它首先递归地列出所有内容,然后进行筛选,所以性能非常差。对于我当前的数据集 25秒 ...

    正在使用listStatus(…)

    代码:

    FileStatus[] statuses = filesystem.listStatus(new Path("/schemas_folder"), new PathFilter()
    {
        private final Pattern pattern = Pattern.compile("^date=[0-9]{8}$");
    
        @Override
        public boolean accept(Path path)
        {
            return pattern.matcher(path.getName()).matches();
        }
    });
    Path[] paths = new Path[statuses.length];
    for (int i = 0; i < statuses.length; i++) { paths[i] = statuses[i].getPath(); }
    statuses = filesystem.listStatus(paths, new PathFilter()
    {
        @Override
        public boolean accept(Path path)
        {
            return "A-schema.avsc".equals(path.getName());
        }
    });
    for (FileStatus status : statuses)
    {
        System.out.println(status.getPath());
    }
    

    结果:

    感谢 PathFilter s和数组的使用,它的性能似乎更快 12秒 ). 然而,代码更复杂,更难以适应不同的情况。最重要的是,性能仍然比命令行版本慢3到4倍!


    问题

    我在这里缺少什么?获得我想要的结果的最快方法是什么?


    更新

    2014.07.09 - 13:38

    拟议的 answer 属于 Mukesh S 显然是最好的API方法。

    在我上面给出的示例中,代码看起来像这样:

    FileStatus[] statuses = filesystem.globStatus(new Path("/schemas_folder/date=*/A-schema.avsc"));
    for (FileStatus status : statuses)
    {
        System.out.println(status.getPath());
    }
    

    这是迄今为止我能想到的最漂亮、性能最好的代码,但性能仍然不如shell版本。

    1 回复  |  直到 9 年前
        1
  •  36
  •   Mukesh S    12 年前

    您可以尝试hadoops globStatus代替listStatus。Hadoop提供了两种处理globs的FileSystem方法:

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

    可以指定可选的PathFilter以进一步限制匹配。

    有关详细说明,请查看Hadoop:Definitive Guide here

    希望它有帮助。。!!!