web-dev-qa-db-ja.com

API呼び出しをリストするHadoopのFileSystemのワイルドカード

_tl;dr:_ワイルドカード(グロブ)を使用できるようにする)リストされたパスでは、 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秒)。 ..


私がこれまでに見つけたもの

各結果の前に1回ずつ、2回_Found 1 items_を出力することに気付くでしょう。最初に_Found 2 items_を1回印刷することはありません。これはおそらく、ワイルドカードが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());
}
_

結果:

PathFiltersと配列の使用のおかげで、パフォーマンスが向上しているようです(約12秒)。ただし、コードはより複雑で、さまざまな状況に適応するのがより困難です。最も重要なことは、パフォーマンスがコマンドラインバージョンよりも3〜4倍遅いことです。


質問

ここで何が欠けていますか?私が望む結果を得るための最速の方法は何ですか?


更新

2014.07.09-13:38

提案された answer of Mukesh S は、明らかに最良のAPIアプローチです。

上記の例では、コードは次のようになります。

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

これは私がこれまでに思いついた中で最高の見た目と最高のパフォーマンスのコードですが、それでもシェルバージョンほどパフォーマンスは良くありません。

16
snooze92

ListStatusの代わりに、hadoopsglobStatusを試すことができます。 Hadoopは、グロブを処理するための2つのFileSystemメソッドを提供します。

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

オプションのPathFilterを指定して、一致をさらに制限できます。

詳細については、Hadoop:Definitive Guide here を確認してください。

それが役に立てば幸い..!!!

28
Mukesh S