Як поділяються етапи на завдання в Spark?


143

Припустимо для наступного, що у кожний момент часу виконується лише одне завдання Spark.

Що я отримую поки що

Ось що я розумію, що відбувається в Spark:

  1. Коли а SparkContextстворюється, кожен вузол робітника запускає виконавця. Виконавці - це окремі процеси (JVM), які підключаються назад до драйверної програми. Кожен виконавець має банку програми драйвера. Вийшовши з водія, відключає виконавців. Кожен виконавець може мати деякі розділи.
  2. Коли завдання виконується, створюється план виконання відповідно до графіка рядків.
  3. Завдання для виконання розбивається на етапи, де етапи містять стільки сусідніх (у графіку рядків) перетворень та дій, але ніяких переміщень. Таким чином етапи розділяються перемичками.

зображення 1

Я розумію, що

  • Завдання - це команда, що надсилається від драйвера виконавцю шляхом серіалізації об'єкта функції.
  • Виконавець десеріалізує (за допомогою драйвера) команду (завдання) та виконує її на розділі.

але

Питання (и)

Як я розділити етап на ті завдання?

Конкретно:

  1. Чи задачі визначаються перетвореннями та діями чи можуть бути декілька перетворень / дій у завданні?
  2. Чи визначаються завдання розділом (наприклад, одне завдання на етап на розділ).
  3. Чи задачі визначаються вузлами (наприклад, одне завдання на етап на вузол)?

Що я думаю (лише часткова відповідь, навіть якщо це правильно)

У https://0x0fff.com/spark-architecture-shuffle , переміщення пояснюється зображенням

введіть тут опис зображення

і у мене складається враження, що це правило

кожен етап розбивається на # завдання на кількість розділів, не враховуючи кількість вузлів

Для свого першого зображення я б сказав, що у мене буде 3 завдання на карті та 3 завдання для зменшення.

Щодо зображення з 0x0fff, я б сказав, що існує 8 завдань на карті та 3 завдання зменшення (якщо припустити, що є лише три помаранчеві та три темно-зелені файли).

Відкривайте запитання в будь-якому випадку

Це правильно? Але навіть якщо це правильно, на всі мої запитання вище не всі відповіді, оскільки це все ще відкрито, чи багато операцій (наприклад, декілька карт) є в межах одного завдання, або розділені на одне завдання на одну операцію.

Що кажуть інші

Що таке завдання в Spark? Як працівник Spark виконує файл jar? і як планувальник Apache Spark розділяє файли на завдання? подібні, але я не відчував, що там на моє запитання чітко відповіли.

Відповіді:


52

У вас тут досить приємний контур. Щоб відповісти на ваші запитання

  • Окремою task ж потрібно бути запущений для кожного розділу даних для кожного stage. Врахуйте, що кожен розділ, ймовірно, буде розташовуватися на різних фізичних місцях - наприклад, блоки в HDFS або каталоги / томи для локальної файлової системи.

Зауважте, що подання Stages визначається за допомогою DAG Scheduler. Це означає, що етапи, які не є взаємозалежними, можуть бути подані кластеру для виконання паралельно: це максимально забезпечує можливість паралелізації на кластері. Отже, якщо операції в нашому потоці даних можуть відбуватися одночасно, ми очікуємо запуску декількох етапів.

Це ми можемо бачити в наступному іграшковому прикладі, в якому ми робимо такі види операцій:

  • завантажте два джерела даних
  • виконати деяку операцію з картою на обох джерелах даних окремо
  • приєднуйтесь до них
  • виконати деякі операції з картою та фільтруванням результату
  • зберегти результат

Тоді на скільки етапів ми закінчимось?

  • 1 етап для завантаження двох джерел даних паралельно = 2 етапи
  • Третій етап , який представляє , joinщо це залежить від двох інших етапів
  • Примітка: всі подальші операції, що працюють над об'єднаними даними, можуть виконуватися на одному етапі, оскільки вони повинні відбуватися послідовно. Немає користі для запуску додаткових етапів, оскільки вони не можуть розпочати роботу до завершення попередньої операції.

Ось та програма з іграшками

val sfi  = sc.textFile("/data/blah/input").map{ x => val xi = x.toInt; (xi,xi*xi) }
val sp = sc.parallelize{ (0 until 1000).map{ x => (x,x * x+1) }}
val spj = sfi.join(sp)
val sm = spj.mapPartitions{ iter => iter.map{ case (k,(v1,v2)) => (k, v1+v2) }}
val sf = sm.filter{ case (k,v) => v % 10 == 0 }
sf.saveAsTextFile("/data/blah/out")

І ось DAG результату

введіть тут опис зображення

Тепер: скільки завдань ? Кількість завдань повинна дорівнювати

Сума ( Stage* #Partitions in the stage)


2
Дякую! Будь ласка, розкажіть свою відповідь стосовно мого тексту: 1) Чи моє визначення етапів не є вичерпним? Здається, я пропустив вимогу, щоб етап не міг містити операції, які могли б бути паралельними. Або мій опис суворо має на увазі це? 2) Кількість завдань, які необхідно виконати для завдання, визначається кількістю розділів, але не кількістю процесорів чи вузлів, тоді як кількість завдань, які можуть бути виконані одночасно, залежить від кількості процесори, правда? 3) Завдання може містити кілька операцій?
Make42

1
4) Що ви мали на увазі під своїм останнім реченням? Зрештою, числові розділи можуть змінюватись від стадії до етапу. Ви мали на увазі, що саме так ви налаштували свою роботу на всі етапи?
Make42

@ Make42 Звичайно, кількість розділів може змінюватись від стадії до етапу - ви праві. Я мав намір сказати sum(..)врахувати цю різницю.
javadba

Вау, ваша відповідь була цілком нормальною, але, на жаль, останнє речення, безумовно, неправильне поняття. Це не означає, що номери розділів на етапі дорівнюють кількості процесорів, однак ви можете встановити кількість розділів для RDD відповідно до кількості ядер, представлених на вашій машині.
epcpu

@epcpu Це був особливий випадок - але я погоджуюся, що це введено в оману, тому я його знімаю.
javadba

26

Це може допомогти вам краще зрозуміти різні твори:

  • Етап: це сукупність завдань. Один і той же процес, що працює проти різних підмножин даних (розділів).
  • Завдання: являє собою одиницю роботи над розділом розподіленого набору даних. Отже, на кожному етапі кількість завдань = кількість розділів або, як ви сказали, «одна задача на етап на розділ».
  • Кожен виконавець працює на одному контейнері пряжі, а кожен контейнер знаходиться на одному вузлі.
  • Кожен етап використовує декілька виконавців, кожному виконавцю виділяється кілька вкорінень.
  • Кожен vcore може виконати рівно одне завдання за один раз
  • Тож на будь-якому етапі паралельно можна виконати кілька завдань. кількість завдань, що виконуються = число вбитості використовується.

2
Це дійсно корисне прочитання про іскрову
pedram bashiri

Я не отримав вашу точку № 3. Наскільки я знаю, кожен вузол може мати декількох виконавців, тож згідно з пунктом 3: На кожний вузол повинен бути лише один виконавець. Чи можете ви уточнити цей момент?
Rituparno Behera

@RituparnoBehera кожен вузол може мати неодноразові контейнери і, таким чином, кілька виконавців Spark. Перевірте це посилання. docs.cloudera.com/runtime/7.0.2/running-spark-applications/…
pedram bashiri

15

Якщо я правильно розумію, є 2 (пов'язані) речі, які вас бентежать:

1) Що визначає зміст завдання?

2) Що визначає кількість завдань, які потрібно виконати?

Двигун Spark "склеює" прості операції на послідовних rdds, наприклад:

rdd1 = sc.textFile( ... )
rdd2 = rdd1.filter( ... )
rdd3 = rdd2.map( ... )
rdd3RowCount = rdd3.count

тому, коли rdd3 (ліниво) обчислюється, іскра генерує завдання на розділ rdd1, і кожне завдання буде виконувати як фільтр, так і карту в рядку, в результаті чого буде rdd3.

Кількість завдань визначається кількістю розділів. Кожен RDD має певну кількість розділів. Для вихідного RDD, який зчитується з HDFS (наприклад, використовуючи sc.textFile (...)), кількість розділів - це кількість розщеплених, створених формою введення. Деякі операції над RDD (ими) можуть призвести до RDD з різною кількістю розділів:

rdd2 = rdd1.repartition( 1000 ) will result in rdd2 having 1000 partitions ( regardless of how many partitions rdd1 had ).

Ще один приклад - приєднання:

rdd3 = rdd1.join( rdd2  , numPartitions = 1000 ) will result in rdd3 having 1000 partitions ( regardless of partitions number of rdd1 and rdd2 ).

(Більшість) операцій, які змінюють кількість розділів, включають перестановку. Коли ми це робимо, наприклад:

rdd2 = rdd1.repartition( 1000 ) 

що насправді відбувається, це завдання для кожного розділу rdd1 потрібно створити кінцевий результат, який можна прочитати на наступному етапі, щоб змусити rdd2 мати рівно 1000 розділів (як це зробити? Hash or Sort ). Завдання з цієї сторони іноді називають "завданням на карті (стороні)". Завдання, яке згодом буде виконуватися на rdd2, буде діяти на одному розділі (з rdd2!) І повинно з’ясувати, як читати / комбінувати виходи на карті, що стосуються цього розділу. Завдання на цій стороні іноді називають "Скорочення (побічні) завдання".

Два питання пов'язані між собою: кількість завдань на етапі - це кількість розділів (загальна для послідовних rdds, "склеєних" разом), а кількість розділів rdd може змінюватися між етапами (зазначаючи кількість розділів для деяких наприклад, переміщення, що викликає роботу).

Після того, як розпочнеться виконання етапу, його завдання можуть займати місця завдань. Кількість одночасних слотів завдань - кількістьExecutor * ExecutorCores. Загалом, вони можуть бути зайняті завданнями з різних, незалежних етапів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.