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
Проблема
Я не хочу использовать команду оболочки и не могу найти эквивалент этой команды выше в 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
Предлагаемый ответ Mukesh S, по-видимому, является наилучшим возможным API-интерфейсом.
В приведенном выше примере конец кода выглядит следующим образом:
FileStatus[] statuses = filesystem.globStatus(new Path("/schemas_folder/date=*/A-schema.avsc"));
for (FileStatus status : statuses)
{
System.out.println(status.getPath());
}
Это лучший и эффективный код, который я мог бы придумать до сих пор, но по-прежнему не выполняет, а также версию оболочки.