Іскра - переділ () проти злиття ()


254

За даними Learning Spark

Майте на увазі, що перерозподіл даних - це досить дорога операція. Spark також має оптимізовану версію repartition()виклику, 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.
Praveen Sripati

12
Чи є випадок, коли repartitionслід використовувати замість цього coalesce?
Нієманд

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 створений для роботи з розділами однакового розміру.

Примітка: Я з цікавістю зазначив, що переділ може збільшити розмір даних на диску . Не забудьте запустити тести, коли ви використовуєте переділ / злиття на великих наборах даних.

Прочитайте цю публікацію в блозі, якщо хочете ще більше деталей.

Коли ви будете використовувати coalesce і переділ на практиці


8
Чудова відповідь @ Повноваження, але чи не перекосовані дані в розділах A і B? Як воно розподіляється рівномірно?
переддень

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

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

хороший! це критично, і, принаймні, для цього досвідченого скала-розробника, не очевидно - тобто ні переділ, ні злиття намагаються змінити дані, як саме вони розподіляються по вузлах
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, коалесценція вічно закінчувалась, тоді як перерозподіл був набагато швидшим і тому вищенаведений код.

Звичайно, ця кількість (20) буде залежати від кількості працівників та кількості даних.

Сподіваюся, що це допомагає.


6

Перерозподіл : перемістіть дані на НОВУ кількість розділів.

Напр. Початковий фрейм даних розділений на 200 розділів.

df.repartition(500): Дані будуть переміщені з 200 розділів на нові 500 розділів.

Coalesce : переміщуйте дані на існуючу кількість розділів.

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


4

Я хотів би додати відповіді Джастіна і Пауера, що -

repartitionбуде ігнорувати наявні розділи та створювати нові. Таким чином, ви можете використовувати його для виправлення перекосу даних. Ви можете згадати ключі розділів для визначення розподілу. Перекос даних - одна з найбільших проблем у проблемному просторі "великих даних".

coalesceбуде працювати з існуючими розділами та переміщувати підмножину з них. Він не може виправити перекос даних так само, як і repartitionвін. Тому навіть якщо вона дешевша, це може бути не потрібною вами справою.


3

До всіх чудових відповідей я хотів би додати, що repartitionце найкращий варіант, щоб скористатися паралелізацією даних. Покиcoalesce дає дешевий варіант зменшити розділи, і це дуже корисно, коли записувати дані в HDFS або інший раковину, щоб скористатися великими записами.

Я вважаю це корисним при написанні даних у форматі паркету, щоб отримати повну перевагу.


2

Для тих, у кого виникли проблеми з генеруванням одного файлу csv з PySpark (AWS EMR) як виводу та збереження його на s3, використовуючи переділ. Причина в тому, що злиття не може зробити повне переміщення, але переділ може. По суті, ви можете збільшити або зменшити кількість розділів, використовуючи переділ, але можна лише зменшити кількість розділів (але не 1), використовуючи coalesce. Ось код для всіх, хто намагається написати csv з AWS EMR до s3:

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

0

Простим способом COALESCE: - призначений лише для зменшення кількості розділів, без перемішування даних, він просто стискає розділи

РЕПАРТАЦІЯ: - це як для збільшення, так і для зменшення кількості розділів, але переміщення відбувається

Приклад: -

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.