Spark - repartition () против coalesce ()


254

По данным Learning Spark

Имейте в виду, что перераспределение ваших данных является довольно дорогой операцией. Spark также имеет оптимизированную версию repartition()call, coalesce()которая позволяет избежать перемещения данных, но только если вы уменьшаете количество разделов RDD.

Одно из различий, которое я получаю, заключается в том, что с repartition()количеством разделов можно увеличивать / уменьшать, но с coalesce()количеством разделов можно только уменьшаться.

Если разделы распределены по нескольким машинам и coalesce()запущены, как можно избежать перемещения данных?

Ответы:


354

Это позволяет избежать полного перемешивания. Если известно, что число уменьшается, то исполнитель может безопасно хранить данные о минимальном количестве разделов, перемещая данные только с дополнительных узлов на те узлы, которые мы сохранили.

Итак, это будет примерно так:

Node 1 = 1,2,3
Node 2 = 4,5,6
Node 3 = 7,8,9
Node 4 = 10,11,12

Затем coalesceдо 2 разделов:

Node 1 = 1,2,3 + (10,11,12)
Node 3 = 7,8,9 + (4,5,6)

Обратите внимание, что Узел 1 и Узел 3 не требовали перемещения своих исходных данных.


115
Спасибо за ответ. Документация должна была лучше сказать, minimize data movementа не avoiding data movement.
Правин Срипати

12
Есть ли случай, когда repartitionследует использовать вместо coalesce?
Niemand

21
@ Niemand Я думаю, что текущая документация достаточно хорошо это освещает: github.com/apache/spark/blob/… Имейте в виду, что все, что repartitionнужно, это вызывать coalesceс shuffleпараметром, установленным в true. Дайте мне знать, если это поможет.
Джастин Пихони

2
Можно ли уменьшить количество существующих файлов разделов? У меня нет hdfs, но проблема со многими файлами.

2
передел будет статистически медленнее, так как он не знает, что он сокращается ... хотя, возможно, они могли бы оптимизировать это. Внутренне это просто вызывает объединение с shuffle = trueфлагом
Джастин Пихони

172

Ответ Джастина потрясающий, и этот ответ углубляется.

repartitionАлгоритм делает полный перетасовать и создает новые разделы с данными, распределенными равномерно. Давайте создадим DataFrame с номерами от 1 до 12.

val x = (1 to 12).toList
val numbersDf = x.toDF("number")

numbersDf содержит 4 раздела на моей машине.

numbersDf.rdd.partitions.size // => 4

Вот как данные делятся на разделы:

Partition 00000: 1, 2, 3
Partition 00001: 4, 5, 6
Partition 00002: 7, 8, 9
Partition 00003: 10, 11, 12

Давайте сделаем полное перемешивание с repartitionметодом и получим эти данные на двух узлах.

val numbersDfR = numbersDf.repartition(2)

Вот как numbersDfRданные разделены на моей машине:

Partition A: 1, 3, 4, 6, 7, 9, 10, 12
Partition B: 2, 5, 8, 11

repartitionМетод делает новые разделы и равномерно распределяет данные в новых разделах (распределение данных больше даже для больших наборов данных).

Разница между coalesceиrepartition

coalesceиспользует существующие разделы, чтобы минимизировать объем перемешиваемых данных. repartitionсоздает новые разделы и делает полное перемешивание. coalesceприводит к разделам с различными объемами данных (иногда разделам, которые имеют очень разные размеры) и repartitionприводит к разделам примерно одинакового размера.

Есть coalesceили repartitionбыстрее?

coalesceможет работать быстрее, чем repartition, но разделы неравного размера обычно работают медленнее, чем разделы равного размера. Обычно вам необходимо перераспределить наборы данных после фильтрации большого набора данных. Я обнаружил, что repartitionв целом быстрее, потому что Spark создан для работы с разделами одинакового размера.

NB Я с любопытством заметил, что перераспределение может увеличить размер данных на диске . Обязательно запускайте тесты, когда вы используете перераспределение / объединение больших наборов данных.

Прочтите этот пост в блоге, если вы хотите еще больше деталей.

Когда вы будете использовать коалесценцию и передел на практике


8
Отличный ответ @Powers, но не перекошены ли данные в разделах A и B? Как оно распределяется равномерно?
anwartheravian

Кроме того, каков наилучший способ получить размер раздела без получения ошибки OOM. Я использую, rdd.glom().map(len).collect()но это дает много ошибок OOM.
anwartheravian

8
@anwartheravian - Раздел A и Раздел B имеют разные размеры, потому что repartitionалгоритм не распределяет данные одинаково для очень маленьких наборов данных. Я имел обыкновение repartitionорганизовывать 5 миллионов записей в 13 разделах, и каждый файл был между 89,3 МБ и 89,6 МБ - это почти равный!
Полномочия

1
@ Сила выглядит лучше ответа с деталями.
Зеленый,

1
Это объясняет разницу намного лучше. Спасибо!
Абхи

22

Еще один момент, который следует отметить, заключается в том, что основным принципом Spark RDD является неизменность. Перераспределение или объединение создаст новый RDD. Базовый RDD будет продолжать существовать с его первоначальным количеством разделов. Если в случае использования требуется сохранить RDD в кэше, то же самое необходимо сделать для вновь созданного RDD.

scala> pairMrkt.repartition(10)
res16: org.apache.spark.rdd.RDD[(String, Array[String])] =MapPartitionsRDD[11] at repartition at <console>:26

scala> res16.partitions.length
res17: Int = 10

scala>  pairMrkt.partitions.length
res20: Int = 2

хороший! это важно и, по крайней мере, для этого опытного разработчика scala, не очевидно - то есть, ни перераспределение, ни объединение попыток изменить данные, только то, как они распределены по узлам
doug

1
@Harikrishnan, так что, если я правильно понял другие ответы, то в соответствии с ними в случае объединения Spark использует существующие разделы, однако, поскольку RDD является неизменным, вы можете описать, как Coalesce использует существующие разделы? Согласно моему пониманию, я думал, что Spark добавляет новые разделы к существующим разделам в коалесции.
Исследователь

Но если «старый» RDD больше не используется, как известно по графику выполнения, он будет удален из памяти, если не будет сохранен, не так ли?
Маркус

15

repartition - рекомендуется использовать его при увеличении количества разделов, потому что это предполагает перемешивание всех данных.

coalesce- рекомендуется использовать его при уменьшении количества разделов. Например, если у вас есть 3 раздела и вы хотите уменьшить его до 2, coalesceданные 3-го раздела будут перемещены в раздел 1 и 2. Раздел 1 и 2 останутся в одном контейнере. С другой стороны, repartitionбудет перетасовывать данные во всех разделах, поэтому использование сети между исполнителями будет высоким, и это повлияет на производительность.

coalesceработает лучше, чем repartitionпри сокращении количества разделов.


Полезное объяснение.
Нарендра Мару

11

Что следует из кода и документов документа, coalesce(n)это то же самое, что coalesce(n, shuffle = false)и repartition(n)то же самое, что иcoalesce(n, shuffle = true)

Таким образом, оба coalesceи repartitionмогут быть использованы для увеличения количества разделов

С помощью shuffle = trueвы можете объединиться с большим количеством разделов. Это полезно, если у вас есть небольшое количество разделов, скажем, 100, потенциально с несколькими разделами, которые являются необычно большими.

Еще одно важное замечание: если вы резко уменьшите количество разделов, вам следует рассмотреть возможность использования перемешанной версии coalesce(как repartitionв этом случае). Это позволит выполнять ваши вычисления параллельно на родительских разделах (несколько задач).

Однако, если вы делаете резкое объединение, например numPartitions = 1, это может привести к тому, что ваши вычисления будут выполняться на меньшем количестве узлов, чем вам нравится (например, на одном узле в случае numPartitions = 1). Чтобы избежать этого, вы можете пройти shuffle = true. Это добавит шаг в случайном порядке, но означает, что текущие разделы восходящего потока будут выполняться параллельно (независимо от текущего разделения).

Пожалуйста, также обратитесь к соответствующему ответу здесь


10

Все ответы добавляют некоторые большие знания в этот очень часто задаваемый вопрос.

Так что, согласно традиции графика этого вопроса, вот мои 2 цента.

Я обнаружил, что перераспределение происходит быстрее, чем слияние , в очень конкретном случае.

В моем приложении, когда количество файлов, которые мы оцениваем, ниже определенного порога, перераспределение работает быстрее.

Вот что я имею ввиду

if(numFiles > 20)
    df.coalesce(numFiles).write.mode(SaveMode.Overwrite).parquet(dest)
else
    df.repartition(numFiles).write.mode(SaveMode.Overwrite).parquet(dest)

В приведенном выше фрагменте, если мои файлы были меньше 20, coalesce занимал целую вечность, а перераспределение было намного быстрее, и поэтому приведенный выше код.

Конечно, это число (20) будет зависеть от количества работников и количества данных.

Надеюсь, это поможет.


6

Перераспределение : перетасуйте данные в новое число разделов.

Например. Исходный фрейм данных разделен на 200 разделов.

df.repartition(500): Данные будут перетасовываться с 200 разделов на новые 500 разделов.

Объединить : перетасовать данные в существующее количество разделов.

df.coalesce(5): Данные будут перетасованы из оставшихся 195 разделов в 5 существующих разделов.


4

Я хотел бы добавить к ответу Джастина и Пауэр, что -

repartitionбудет игнорировать существующие разделы и создавать новые. Таким образом, вы можете использовать его для исправления перекоса данных. Вы можете упомянуть ключи разделов, чтобы определить распределение. Отклонение данных - одна из самых больших проблем в пространстве проблем «больших данных».

coalesceбудет работать с существующими разделами и перетасовывать их подмножество. Это не может исправить перекос данных так же, как repartitionи. Поэтому, даже если это дешевле, это может быть не то, что вам нужно.


3

Ко всем отличным ответам я хотел бы добавить, что repartitionэто один из лучших вариантов использования распараллеливания данных. Покаcoalesce это недорогая опция для сокращения разделов, она очень полезна при записи данных в HDFS или другие приемники, чтобы использовать преимущества больших записей.

Я нашел это полезным при записи данных в формате паркета, чтобы получить полное преимущество.


2

Для кого-то, у кого были проблемы с генерацией одного CSV-файла из PySpark (AWS EMR) в качестве вывода и сохранением его на s3, использование перераспределения помогло. Причина в том, что объединение не может сделать полное перемешивание, но перераспределение может. По сути, вы можете увеличить или уменьшить количество разделов, используя перераспределение, но можете только уменьшить количество разделов (но не 1), используя объединение. Вот код для тех, кто пытается записать CSV из AWS EMR в s3:

df.repartition(1).write.format('csv')\
.option("path", "s3a://my.bucket.name/location")\
.save(header = 'true')

0

Проще говоря, COALESCE: - предназначен только для уменьшения количества разделов, без перемешивания данных, он просто сжимает разделы

REPARTITION: - предназначен как для увеличения, так и для уменьшения количества перегородок, но происходит перетасовка

Пример:-

val rdd = sc.textFile("path",7)
rdd.repartition(10)
rdd.repartition(2)

Оба отлично работает

Но обычно мы идем к этим двум вещам, когда нам нужно увидеть выходные данные в одном кластере, мы идем с этим.


9
Там будет движение данных с Coalese также.
sun_dare

0

Но также вы должны убедиться, что данные, поступающие на объединяющиеся узлы, должны быть хорошо сконфигурированы, если вы имеете дело с огромными данными. Поскольку все данные будут загружены в эти узлы, может возникнуть исключение памяти. Хотя возмещение затратно, я предпочитаю им пользоваться. Так как он перемешивает и распределяет данные одинаково.

Будь мудрым, чтобы выбрать между объединением и переделом.


0

repartitionАлгоритм делает полную перетасовку данных и создает равные по размеру разделов данных.coalesceобъединяет существующие разделы, чтобы избежать полного перемешивания.

Coalesce хорошо работает для получения RDD с большим количеством разделов и объединения разделов на одном рабочем узле для создания окончательного RDD с меньшим количеством разделов.

Repartitionпроизведет перестановку данных в вашем RDD для получения окончательного количества разделов, которые вы запрашиваете. Разделение DataFrames похоже на детали реализации низкого уровня, которыми должна управлять инфраструктура, но это не так. При фильтрации больших DataFrames в меньшие, вы почти всегда должны перераспределять данные. Вы, вероятно, будете часто фильтровать большие DataFrames в меньшие, так что привыкните к перераспределению.

Прочтите этот пост в блоге, если вы хотите еще больше деталей.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.