web-dev-qa-db-fra.com

Comment les étapes sont-elles divisées en tâches dans Spark?

Supposons pour les suivants qu'un seul travail Spark est en cours d'exécution à tout moment.

Ce que j'arrive jusqu'à présent

Voici ce que je comprends de ce qui se passe dans Spark:

  1. Lorsqu'un SparkContext est créé, chaque nœud de travail démarre un exécuteur. Les exécuteurs sont des processus distincts (JVM), qui se reconnectent au programme du pilote. Chaque exécuteur a le pot du programme du pilote. Quitter un pilote, ferme les exécuteurs. Chaque exécuteur peut contenir des partitions.
  2. Lorsqu'un travail est exécuté, un plan d'exécution est créé conformément au graphique de lignage.
  3. Le travail d’exécution est divisé en étapes, les étapes contenant autant de transformations et d’actions voisines (dans le graphe de lignage), mais pas de réorganisation. Ainsi, les étapes sont séparées par des shuffles.

image 1

Je comprends que

  • Une tâche est une commande envoyée par le pilote à un exécuteur en sérialisant l'objet Function.
  • L'exécuteur désérialise (avec le pilote jar) la commande (tâche) et l'exécute sur une partition.

mais

Des questions)

Comment diviser la scène en tâches?

Plus précisément:

  1. Les tâches sont-elles déterminées par les transformations et les actions ou peuvent-elles être plusieurs transformations/actions dans une tâche?
  2. Les tâches sont-elles déterminées par la partition (par exemple, une tâche par étape et par partition)?.
  3. Les tâches sont-elles déterminées par les nœuds (par exemple, une tâche par étape et par nœud)?

Ce que je pense (réponse partielle, même si c'est vrai)

Dans https://0x0fff.com/spark-architecture-shuffle , la lecture aléatoire est expliquée avec l'image

enter image description here

et j'ai l'impression que la règle est

chaque étape est divisée en # tâches de nombre de partitions, sans tenir compte du nombre de nœuds

Pour ma première image, je dirais que j'aurais 3 tâches de carte et 3 tâches de réduction.

Pour l'image à partir de 0x0fff, je dirais qu'il y a 8 tâches de carte et 3 tâches de réduction (en supposant qu'il n'y ait que trois fichiers orange et trois fichiers vert foncé).

Des questions ouvertes dans tous les cas

Est-ce exact? Mais même si cela est correct, toutes les questions ci-dessus ne répondent pas à toutes les questions, car il est toujours ouvert, que plusieurs opérations (par exemple plusieurs cartes) relèvent d'une tâche ou soient séparées en une tâche par opération.

Ce que disent les autres

Qu'est-ce qu'une tâche dans Spark? Comment le travailleur Spark exécute-t-il le fichier jar? et Comment le planificateur Apache Spark _ scindé tâches? sont similaires, mais je ne pensais pas avoir répondu clairement à ma question.

111
Make42

Vous avez un joli joli contour ici. Pour répondre à tes questions

  • Un taskne distinct doit être lancé pour chaque partition de données pour chaque stage. Considérez que chaque partition réside probablement dans des emplacements physiques distincts - p. Ex. blocs dans HDFS ou répertoires/volumes pour un système de fichiers local.

Notez que la soumission de Stages est gérée par le DAG Scheduler. Cela signifie que les étapes non interdépendantes peuvent être soumises au cluster pour une exécution en parallèle: cela maximise la capacité de parallélisation sur le cluster. Donc, si des opérations dans notre flux de données peuvent se produire simultanément, nous nous attendons à voir plusieurs étapes lancées.

Nous pouvons le voir en action dans l'exemple de jouet suivant dans lequel nous effectuons les types d'opérations suivants:

  • charger deux sources de données
  • effectuer des opérations de carte sur les deux sources de données séparément
  • rejoins-les
  • effectuer des opérations de cartographie et de filtrage sur le résultat
  • enregistrer le résultat

Alors combien d’étapes finirons-nous?

  • 1 étape chacune pour charger les deux sources de données en parallèle = 2 étapes
  • Une troisième étape représentant la join qui est dépendante sur les deux autres étapes
  • Remarque: toutes les opérations suivantes travaillant sur les données jointes peuvent être effectuées à l’étape même car elles doivent se dérouler de manière séquentielle. Il n’ya aucun avantage à lancer des étapes supplémentaires car elles ne peuvent pas commencer les travaux avant la fin de l’opération précédente.

Voici ce programme de jouet

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")

Et voici le DAG du résultat

enter image description here

Maintenant: combien de tâches ? Le nombre de tâches doit être égal à

Somme de (Stage * #Partitions in the stage)

40
javadba

Cela pourrait vous aider à mieux comprendre différentes pièces:

  • Stage: est un ensemble de tâches. Même processus exécuté sur différents sous-ensembles de données (partitions).
  • Tâche: représente une unité de travail sur une partition d'un ensemble de données distribué. Donc, à chaque étape, nombre de tâches = nombre de partitions, ou comme vous l'avez dit "une tâche par étape par partition".
  • Chaque exécuteur s'exécute sur un conteneur de fil et chaque conteneur réside sur un nœud.
  • Chaque étape utilise plusieurs exécutants, chaque exécutant se voit attribuer plusieurs vcores.
  • Chaque vcore peut exécuter exactement une tâche à la fois
  • Ainsi, à tout moment, plusieurs tâches peuvent être exécutées en parallèle. nombre de tâches en cours d'exécution = nombre de vcores utilisées.
16
pedram bashiri

Si je comprends bien, il y a 2 choses (liées) qui vous déroutent:

1) Qu'est-ce qui détermine le contenu d'une tâche?

2) Qu'est-ce qui détermine le nombre de tâches à exécuter?

Le moteur de Spark "colle" ensemble des opérations simples sur des disques consécutifs, par exemple:

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

ainsi, lorsque rdd3 est calculé (paresseusement), spark générera une tâche par partition de rdd1 et chaque tâche exécutera à la fois le filtre et la carte par ligne pour générer rdd3.

Le nombre de tâches est déterminé par le nombre de partitions. Chaque RDD a un nombre défini de partitions. Pour un RDD source lu à partir de HDFS (à l'aide de sc.textFile (...), par exemple), le nombre de partitions est le nombre de divisions générées par le format d'entrée. Certaines opérations sur un ou plusieurs RDD peuvent aboutir à un RDD avec un nombre de partitions différent:

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

Un autre exemple est joint:

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

(La plupart) des opérations qui changent le nombre de partitions impliquent un remaniement, quand on fait par exemple:

rdd2 = rdd1.repartition( 1000 ) 

ce qui se passe réellement, c’est que la tâche sur chaque partition de rdd1 doit produire une sortie qui peut être lue à l’étape suivante de manière à ce que rdd2 ait exactement 1000 partitions (comment elles le font? Hash ou - Trier ). Les tâches de ce côté sont parfois appelées "tâches de carte (côté)". Une tâche qui sera exécutée ultérieurement sur rdd2 agira sur une partition (de rdd2!) Et devra trouver comment lire/combiner les sorties côté carte correspondant à cette partition. Les tâches de ce côté sont parfois appelées "tâches réduites".

Les 2 questions sont liées: le nombre de tâches dans une étape est le nombre de partitions (communes aux disques consécutifs "collés") et le nombre de partitions d’un RDD peut changer entre les étapes (en spécifiant le nombre de partitions mélange aléatoire provoquant une opération par exemple).

Une fois que l'exécution d'une étape commence, ses tâches peuvent occuper des emplacements de tâches. Le nombre d'emplacements de tâches simultanés est numExecutors * ExecutorCores. En général, ceux-ci peuvent être occupés par des tâches provenant de différentes étapes non dépendantes.

12
Harel Gliksman