Несколько заданий spark, добавляющих данные parquet к одному базовому пути с разделением
У меня есть несколько заданий, которые я хочу выполнить параллельно, что дописать данные в один и тот же путь, используя перегородки.
например
dataFrame.write().
partitionBy("eventDate", "category")
.mode(Append)
.parquet("s3://bucket/save/path");
Задание 1-категория = " billing_events" Задание 2-категория = "click_events"
оба этих задания будут усекать все существующие разделы, которые существуют в ведре s3 до выполнения, а затем сохранять полученные файлы паркета в соответствующие разделы.
то есть
задание 1 - > s3://bucket/save/path / eventDate=20160101 / channel=billing_events
Задание 2 - > s3://bucket/save/path / eventDate=20160101 / channel=click_events
проблема im сталкивается с временными файлами, которые создаются во время выполнения задания spark. Он сохраняет рабочие файлы в базовый путь
s3://bucket/save/path / _temporary/...
таким образом, оба задания в конечном итоге разделяют одну и ту же папку temp и вызывают конфликт, который, как я заметил, может привести к одному заданию удалите временные файлы, и другое задание завершится неудачей с 404 из s3, говоря, что ожидаемый временный файл не существует.
кто-нибудь сталкивался с этой проблемой и придумать стратегию параллельного выполнения заданий в той же пути?
im с помощью spark 1.6.0 на данный момент
3 ответов
поэтому после долгого чтения о том, как решить эту проблему, я подумал, что я передаю некоторую мудрость здесь, чтобы обернуть вещи. В основном благодаря комментариям Тала.
Я также обнаружил, что запись непосредственно в s3: / / bucket / save / path кажется опасной, потому что, если задание убито, а очистка временной папки не происходит в конце задания, кажется, что его оставили там для следующего задания, и я заметил, что иногда файлы предыдущих убитых заданий temp попадают в s3: / / bucket / save / path и вызывает дублирование... Абсолютно ненадежно...
кроме того, операция переименования файлов папки _temporary в соответствующие файлы s3 занимает ужасное количество времени (около 1 сек на файл), поскольку S3 поддерживает только копирование/удаление, а не переименование. Кроме того, только экземпляр драйвера переименовывает эти файлы с помощью одного потока, так как 1/5 некоторых заданий с большим количеством файлов / разделов тратятся только на ожидание переименования оперативный.
Я исключил использование DirectOutputCommitter по ряду причин.
- при использовании в сочетании с режимом спекуляции это приводит к дублированию (https://issues.apache.org/jira/browse/SPARK-9899)
- задач неудачи оставят беспорядок, который было бы невозможно найти и удалить/очистить позже.
- Spark 2.0 полностью удалил поддержку этого и не обновил путь существует.(https://issues.apache.org/jira/browse/SPARK-10063)
единственный безопасный, эффективный и согласованный способ выполнения этих заданий-сначала сохранить их в уникальной временной папке (уникальной по applicationId или timestamp) в hdfs. И скопируйте в S3 по завершении задания.
Это позволяет выполнять параллельные задания, поскольку они будут сохранены в уникальных временных папках, не нужно использовать DirectOutputCommitter, поскольку операция переименования в HDFS быстрее, чем S3, и сохраненные данные более последовательны.
Я подозреваю, что это связано с изменениями в обнаружении разделов, которые были введены в Spark 1.6. Изменения означают, что Spark будет обрабатывать только такие пути, как .../xxx=yyy/
как разделы, если вы указали параметр "basepath" (см. Примечания к выпуску Spark здесь).
поэтому я думаю, что ваша проблема будет решена, если вы добавите опцию basepath, например:
dataFrame
.write()
.partitionBy("eventDate", "category")
.option("basepath", "s3://bucket/save/path")
.mode(Append)
.parquet("s3://bucket/save/path");
(у меня не было возможности проверить это, но, надеюсь, это сделает трюк:))
вместо использования partitionBy фрейм данных.писать.)( partitionBy ("eventDate", " категория") .режим (добавить) .паркет ("s3: / / bucket / save / path");
в качестве альтернативы вы можете записать файлы как
в задании-1 укажите путь к файлу паркета как фрейм данных.писать.)(режим(добавить).паркет ("s3://bucket/save/path/eventDate=20160101/channel=billing_events")
& в задании-2 укажите путь к файлу паркета как фрейм данных.писать.)(режим(добавить).паркет ("s3://bucket/save/path/eventDate=20160101/channel=click_events")
- оба задания создадут отдельный каталог _temporary в соответствующей папке, поэтому проблема параллелизма будет решена.
- и обнаружение разделов также произойдет как eventDate=20160101 и для столбца канала.
- недостаток-даже если channel=click_events не существует в данных, все равно файл паркета для канала=click_events будет быть созданным.