Hadoop的FileSystem列出API调用中的通配符

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 

就我而言,该目录在不同日期存储不同类型数据(本例中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 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

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版本那么好。

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

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

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

有关更多说明,请在此处查看Hadoop:权威指南

希望能帮助到你..!!!