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

时间:2014-07-09 07:43:24

标签: java hadoop hdfs wildcard

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


上下文

我的HDFS群集上的文件按分区进行组织,日期为“root”分区。文件结构的简化示例如下所示:

/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

就我而言,该目录在不同日期为不同类型的数据(本例中为A,B和C)存储Avro模式。随着时间的推移,模​​式可能会开始存在,发展并停止存在。


目标

我需要能够尽快获得给定类型的所有模式。在我希望得到类型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命令,似乎无法在Java API中找到与上述命令等效的内容。当我尝试自己实现循环时,我的表现非常糟糕。我希望至少命令行的性能(在我的情况下 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和使用数组,它似乎表现得更快(大约 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 个答案:

答案 0 :(得分:27)

您可以尝试hadoops globStatus而不是listStatus。 Hadoop提供了两种用于处理全局的FileSystem方法:

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

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

有关更多说明,您可以查看Hadoop:权威指南here

希望它有所帮助...... !!!