SlideShare une entreprise Scribd logo
Spark
10/08/2015
IST/C3S - Mehdi ERRAKI
Spark : généralités
A propos de spark
• Spark est une plateforme de traitement distribué
désignée pour être :
– rapide : in-memory, shell interactif, traitement itératif
– « general-purpose » : un seul cœur (Spark) exposant des
APIs pour manipuler des collections distribuées de
données (RDD)
• Spark et hadoop :
– Spark peut être installé dans un cluster hadoop (mais pas
nécessairement)
– APIs haut niveau
– Spark remédie à plusieurs défauts de Mapreduce
Spark Stack
Spark core :
• fonctionnalités basiques de spark : scheduling des tâches, gestion de mémoire, fault
recovery, interaction avec le système de stockage
• fournit plusieurs API de définition et manipulation des RDD : abstraction primaire des
donnés sous forme de collection distribuée d'items
Spark Stack
Spark SQL:
• interface SQL qui s'intègre avec le spark core dans les mêmes applications :
• manipulation SQL des données structurées + analyse avancées des rdd avec scala python ou
java
Spark Stack
Spark MLIB:
• Classification, régression, clustering, collaborative filtering
• évaluation des modèles
• des primitives bas niveau : « gradient descent algo »
• toutes désignées pour le « scaling out » a travers plusieurs noeuds
Spark Stack
Spark Streaming:
• composant spark pour traiter les flux live de données : logfiles .. l'API streaming est proche de
celle de spark’s core RDD API. Ceci facilite la transition pour un développeur qui programme
des traitements sur des données en mémoire ou en disque , à des traitement en temps réel
Spark Stack
Spark Graphx:
• bibliothèque pour manipuler des graphes , étend l’ API spark RDD comme streaming et SQL
Cluster managers
• Spark peut continuer de « scaler » a travers un cluster de plusieurs milliers de nœuds.
• Il supporte plusieurs clusters managers :
– Hadoop yarn
– Apache mesos
– Standalone scheduler : empty set of machines
– local : tout les processus spark dans un seul thread
Spark Stack
Stockage
• Spark peut utiliser tous les systèmes de stockage supportés par l’API de Hadoop:
– HDFS
– S3
– Local FS
– Cassandra
– Hive
– Hbase
• INPUT et OUTPUT Format
– Spark peut accéder en lecture ou en écriture à tous les formats qui utilisent les interfaces InputFormat et
OutputFormat utilisées par Hadoop Mapreduce
Architecture maître-esclave
• Driver program:
– Exécution du main de la classe codée par le développeur
– Initialise un « spark context » : connexion à un « computing cluster »
• Spark Executors :
– Des nœuds esclaves gérés par le driver pour la durée de vie d’un « spark context »
– se chargent d’exécuter des tâches en parallèle
Deux modes d’utilisation – shell
interactif
• Spark-shell
Deux modes d’utilisation – standalone
applications
• Scala project
– Fraud.scala
• Compilation and versionning : sbt tool
– Build.sbt
name := « Fraud_Bypass"
version := "0.0.1"
scalaVersion := "2.10.4 "
libraryDependencies ++="org.apache.spark" %% "spark-core" % "1.2.1"
– Linux shell >> sbt package
• Compilation
– Linux Shell >> spark-submit –class Fraud Fraud_Bypass-0.0.1.jar arg_to_the_main_1 arg_to_the_main_2 …….
Spark : RDDs et plan d’exécution
RDDs
• Un RDD est l’abstraction primaire des données sous SPARK
• Un RDD correspond à une collection non mutable distribuée d’objets, qui peuvent être de n’importe
quel type de données Python, Scala ou Java, ou des “user-defined class”
• Chaque RDD est constitué de plusieurs partitions distribuées sur le cluster.
• Spark distribue automatiquement les RDDs selon le nombre de partitions souhaitées (ou selon un nombre
par défaut)
• Les RDD sont “fault-tolerant”, et bénéficie d’un mécanisme de reconstitution en cas de perte d’une
partition donnée
• Il est possible de créer un RDD :
 En chargeant un collection externe de données par le driver programme (depuis hdfs par exemple)
 Ou en créant un nouveau RDD en appliquant une transformation sur un ancien RDD
 Ou en distribuant une collection d’objets (List, Set..) dans le driver programme
RDDs opérations
• Il existe deux types d’opérations qu’on peut appliquer sur un RDD
– Les transformations : permettent de constituer un nouveau RDD (map– filter – groupByKey – join)
– Les actions : permettent de créer un résultat à partir d’un RDD pour le retourner au driver ou l’écrire en système de
stockage. (count – collect – saveAsTextFile)
• Example :
– A partir d’un RDD de CDRs, une transformation de filtrage permet de créer un nouveau RDD contenant uniquement
les types de « 0 »
– Une fois créée, on applique une action « take(3) » sur le RDD, pour récupérer au niveau du driver un tableau de 3
CDRs
RDDs opérations - transformations
RDDs operations - transformations
LAZY COMPUTING
• Principe :
– Au moment de la création d’un nouveau RDD, Spark se contente de garder en mémoire l’ensemble des étapes nécessaires pour calculer le RDD.
– Il ne le crée physiquement qu’on moment ou il doit être utilisé dans une action
• Avantages :
– Gestion efficace du disque et de la mémoire
– Optimisation des données à charger ( Exemple de succession des transformations : textFile + filter)
– Optimisation des traitements à effectuer pour exécuter une succession de transformations en les regroupant (exemple d’un succession de d’opération de
mapping)
– Corolaire du dernier point : Code plus léger, moins compact, et facilement compréhensible (par rapport à map reduce)
• Inconvénient :
– Si un RDD doit être utilisé dans n actions au sein d’une même application, il sera calculé n fois différentes
– Solution : Il est possible de persister un RDD en mémoire (ou en disque). Celui-ci sera calculé une seule fois, lors de l’exécution de la première action qui
utilise ce RDD, et sera gardé en mémoire de façon distribuée au niveau de tous les exécuteurs utilisés pour le calculer. Lors de l’éxécution d’une nouvelle
action qui utilise ce RDD, celui est accessible rapidement en mémoire, et ne sera pas recalculé.
// spark ne charge pas physiquement les données du RDD
// spark mémorise comment calculer second_RDD
// spark mémorise comment calculer Third_RDD
// spark charge physiquement les données du RDD pour établir
//first_RDD, calcule physiquement « second_RDD » et « third_RDD »
// Il effectue ensuite l’action de comptage, dont la valeur retour est
//retrounée au driver
Persistance
• Scala java – par défault : Stockage en mémoire sous forme d’objets non serialisés
• Python – par défault : Stockage en mémoire sous forme d’objets sérialisés
• Suite à une défaillance d’un noeud, Spark re-calcule les partitions perdues au besoin.
• Il est possible de répliquer les données persistées.
• Si les données à cacher dépassent les capacités de mémoire, Spark supprime les anciennes partitions persistées.
– En mémoire : il re-calcule les parititions supprimées au besoin
– En mémoire-et-disque : Il les écrira en disque
• Pour libérer la mémoire, on peut appeller la fonction “unpersist()”
RDDs opérations - transformations
Deux types de transformations
Il existe deux types de transformations sur les RDDs
• Narrow : tous les enregistrement dans une des partitions du RDD
fils , sont calculés uniquement à partir des enregistrements d’une
et une seule partition du RDD parent
• Wide : La constitution d’une partition fait intervenir plusieurs
partitions
 Le shuffle qui intervient dans toutes les transformations de type
« wide » est une opération très consommatrice en bande passante
au sein du cluster et peut créer un goulot d’étranglement.
 Règle pratique : Il est important d’effectuer toutes les opérations
de filtrage avant les transformations de type « wide », afin d’avoir
le minimum possible de données à « shuffler » entre les noeuds
RDDs opérations - transformations
RDDs opérations – basic actions
- Toutes ces opérations retournent des résultats au driver (sauf foreach() qui est exécuté directement au niveau des « executors » )
 ATTENTION : le retour de la fonction « collect » doit être supporté par une seule machine (celle du driver), et peut
éventuellement causer l’echec du job
Plan d’exécution d’une application spark
Vocabulaire : Application/Job/Stage/Tâche
• Application : un programme qui peut consister en plusieurs transformations, et actions.
• Job : Quand une application Spark est lancée, chaque action sur une RDD engendre un job
• Stage : Un job contient N+1 stages séquentiels si N est le nombre de transformations de type « Wide ».
• Tâches : Chaque stage se déroule en plusieurs tâches, chacune opérant sur une partition de la RDD en entrée du
stage.
• A l’issue d’un shuffle, le nombre de tâches du stage suivant est défini par le développeur ( ou égal à une valeur par
défaut)
map groupByKey
RDDRDD
Stage 1 Stage 2
Spark : mode distribué et YARN
Mode distribué
• Composants d’une application spark
– Spark driver : s’exécute dans son propre process java.
– Spark executors : chacun s’exécute dans son propre java process
• La négociation des ressources se fait entre le driver et le cluster
manager (Yarn, Mesos, ..)
Le driver
- Le driver est un process java , dans lequel s’exécute le main du
programme codé par le développeur.
- Il exécute deux fonctions principales :
• Convertir un programme en un plan d’exécution
– À partir d’un graphe acyclique logique qui caractérise le programme …
– ...le driver établit un plan d’exécution physique sous forme de plusieurs stages,
contenant plusieurs tâches à faire exécuter par le cluster
• Ordonnancer l’exécution du plan d’exécution
– Les exécuteurs s’enregistrent auprès du driver
– Le driver se charge ensuite d’assigner les différentes tâches aux exécuteurs de
façon à maximiser le principe de proximité des données
– Le driver mémorise également les endroits de persistance des différentes
partitions des RDDs (quand elles sont persistées), afin d’optimiser l’assignation
des futures tâches qui accéderont à ces données
Les exécuteurs
• Un exécuteur spark est un process Java qui s’occupe de l’exécution des
tâches que lui assigne le driver. Il est lancé au début de l’application et
reste disponible pendant toute la durée du job. Si un exécuteur défaille,
l’application continue d’être exécutée et un mécanisme de récupération
de données et/ou de tâches est géré par Spark de façon transparente.
• Rappel : Un process java (ici une JVM) peut lancer parallèlement un
nombre maximal de threads (tâches), qui est égal au nombre de cœurs de
processeurs alloués pour le process.
 Il remplit deux fonctions :
– Exécuter les tâches qui leurs sont assignées et retournent des résultats au driver
– Offrir du stockage en mémoire aux RDDs persistées en mémoire
Lancement d’un programme
1. L’utilisateur soumet une application spark par le biais de la
commande spark-submit
2. Le programme driver est lancé dans un process java , et invoque la
métode main du programme lancé par l’utisateur
3. Le programme driver contacte le cluster manager pour allouer des
ressources pour lancer des exécuteurs, coformément aux
ressources demandées par l’utilisateur
4. Le cluster manager lance les exécteurs pour le compte du driver
5. Le driver consitute le plan d’exécution et assigne des tâches
élémentaires aux exécuteurs
6. Le driver se termine quand le main() finit de s’exécuter ou suite à
un appel de SparkContext.stop().
7. Il arrête les éxécuteurs et libère les ressources auprès du cluster
manager.
Le spark-submit
• Le format général :
 bin/spark-submit [options] <app jar | python file> [app options]
• Les options
Rappel : Yarn
Spark sur Yarn : architecture
Mode Yarn-client
Lorsque Yarn est utilisé comme
Cluster Manager, il faudrait bien
distinguer entre les deamons YARN
et les deamons SPARK
YARN
 RM : responsable d’affectation
des ressources
 Spark AM : demande les
ressources auprès du RM
 Spark NM : nœuds contenant un
ou plus de container, chacun
pouvant accueillir un exécuteur
spark.
SPARK
 Spark Driver : s’exécute chez le
client en mode Yarn-client , ou dans
l’application master en mode Yarn-
cluster
 Spark Executor : s’exécutent dans
des containers YARN au sein du
node manager.
ATTENTION : deux exécuteurs spark peuvent
se retrouver dans deux containers Yarn
différents, mais au sein de la même machine
(YARN node manager)
Spark sur Yarn– Application/Job/Stage/Tâche
• Quand une application Spark est lancée, chaque action sur
une RDD engendre un job qui contient N+1 stages séquentiels
si N est le nombre de transformations de type « Wide ».
• Chaque stage se déroule en plusieurs tâches, chacune opérant
sur une partition de la RDD précédente.
• Au sein d’un stage(où toutes les transformations sont de type
« narrow »), le nombre de tâches est égal exactement au
nombre de partitions du dernier RDD constitué, qui lui-même
est (supérieur ou) égal au nombre de partitions du RDD initial
(nombre de blocs hdfs sous-jacents).
• A l’issue d’un shuffle, le nombre de tâches du stage suivant
est défini par le développeur ( ou égal à 1 par défaut)
• Une tâche est exécutée par un cœur processeur d’un
exécuteur spark (container Yarn) alloué pour l’application.
• Les tâches d’un stage se déroulent parallèlement si les
ressources allouées le permettent.
• Deux stages ou jobs indépendants peuvent être exécutés
parallèlement.
 Règle : Afin de paralléliser entièrement les tâches d’un stage contenant des transformations de type « Narrow », le nombre d’exécuteurs
alloués * le nombre de cœurs par exécuteur DOIT ETRE SUPERIEUR OU EGAL au nombre de blocs HDFS du fichier initial
 Règle : En général, Le nombre d’exécuteur alloués * le nombre de cœurs par exécuteur DOIT ETRE SUPERIEUR OU EGAL au nombre de
tâches du stage (ou des stages si ceux-ci sont indépendants et donc parallèles) contenant le plus grand nombre de tâches.
 Règle : Si les ressources le permettent, il est préférable d’en allouer largement au dessus des seuils précédents, afin d’avoir plus de
chances d’appliquer le principe de proximité des données (chaque partition est traitée par un exécuteur lancé dans le même nœud hdfs
contenant le bloc correspondant)
map groupByKey
RDDRDD
Stage 1 Stage 2
Exemple : programme bypass
Objectif
• A partir d’une table de CDRs sous forme d’un fichier CSV
stocké dans hdfs, calculer des agrégats (par msisdn)
– Voice total count
– Voice in total count
– SMS in total count
– Calls dispersion
– Cells disperion
– Voice intenational out
• Comparer ses compteurs à des valeurs seuils, pour filtrer les
comportements fraudeurs
1. Read CDR decoded Data
 Val data = sc.textFile(« path/to/files »)
2. Constructing a paired RDD
 Val paired_data = filtered_data.map( row=> ( row(msisdn) , CDR_Schema_Apply(row) )
3. Grouping_by_key
 Val grouped_by_key_data= paired_data.groupByKey(numOfPartitions)
4. Applying the rule()
 Val fraud_list = grouped_by_key_data.filter( (key,value) => rule_func(value, json) )
5. The driver collects the results in an array of Strings , (before writing back to a Hive (or Hbase table))
 Val result= fraud_list .collect()
Algorithme spark (code sous-jacent en scala)
DAG
Algorithme spark : Object_func
 rule_funct( rule : Rule, cdrs : Iterable[Cdr] ) : Boolean
Return
condition_func(rule.condition(1), cdrs) rule.binaryOperators(1) condition_funct(rule.condition(2),cdrs)
rule.binaryOperators(2) condition_funct(rule.condition(3),cdrs) ……..
condition_func (condition : Condition , cdrs : Iterable[Cdr] ) : Boolean
Condition.comparison_op match {
case « ge » => return operation_funct( condition.operations(1) , cdrs) >= operation_funct( condition.operations(2) ,cdrs)
case « le » => return operation_funct( condition.operations(1) , cdrs) <= operation_funct( condition.operations(2) ,cdrs)
case « g » => return operation_funct( condition.operations(1) , cdrs) > operation_funct( condition.operations(2) , cdrs)
case « l » => return operation_funct( condition.operations(1) , cdrs) < operation_funct( condition.operations(2) , cdrs)
case « eq » => return operation_funct( condition.operations(1) , cdrs) == operation_funct(condition.operations(2) , cdrs)
case « not » => return operation_funct( condition.operations(1) , cdrs) == 0 }
operation_func(Operation : operation , cdrs : Iterable[Cdr] ) : Float
Operation.operation_type match {
case « ratio » => return Function_funct( operations.functions(1) , cdrs) / Function_funct( operations.functions(2) , cdrs)
case « product » => return Function_funct( operations.functions(1) , cdrs) *Function_funct( operations.functions(2) , cdrs)
case « filter » | « static_threshlod » | « counter » => return Function_funct( operations.functions(1) , cdrs)
function_funct (function : Function , cdrs : Iterable[Cdr] ) : Float
Function.function_type match {
case « count » => return cdrs.count(cdr => function.fields.contains(cdr.recordType))
case « static_value » => return function.fields(0).toFloat
case « filter » => return sc.textFile(function.fields(0)).collect().contains(cdr.msisdn)
case « disitinct_count » => countList
map groupByKeytextFile
Stage 1 Stage 2
HDFS csv file (or hive
table) of n blocks
data : RDD [String] : primary data
abstraction in Spark. It contains a
distributed set of lines (rows)
Grouped_by_key_data :
Paired_RDD [(key : String
,value : Iterable[Tuple
[String]])]
paired_data : Paired_RDD [
(key : String , value :
Tuple[String]) ]
Result :
Array
[Strings]
Fraud_list: Paired_RDD [(key : String
,value : Iterable[Tuple [String]])]
collect
(*) : source : Learning spark - O'Reilly Media
filter
 4 blocks => 4 Tasks in
stage 1
num_executors *
executors_cores > N
!!!!!
numPartions
given as in
input to
groupByKey_
function=>
numPartions
Tasks in
stage 2
numOfPari
tions >
queue_total
_cores (=790
for
« HQ_IST »
queue » !!!
(*)
Plan d’exécution
• Supposons que le fichier en entrée est d’une taille de 1Go = 3 * 256Mo + 232Mo (4 blocs hdfs)
• L’application comporte plusieurs transformations sur le RDD et une seule action.
– Un Seul job est donc lancé
– Ce job comporte plusieurs transformations de type Narrow, et une seule transformation de type wide(groupByKey).
– Il comprend donc deux stages séparés par le shuffle
– Le premier stage se déroule sous forme de plusieurs tâches (4 tâches exactement)
– Dans une tâche de ce premier stage, la partition concernée du RDD initial subit toute les transformations du premier stage.
– Le second stage comprend un nombre de tâches exactement égal à la valeur de repartitionnement donnée en paramètre à la fonction
groupByKey (3 partitions dans l’illustration ci-dessous)
Allocation des ressources pour le programme
• Rappel : capacités du cluster de la PEI :
– 2 racks, 55 Nœuds actifs, <memory: 4.83 TB , vCores : 1320>
• Deux types de nœuds : < 58.88 GB , 24 cores > ou < 121.99 GB , 24 cores >
– Capacités maximales pour la queue « HQ_IST »
• Capacité maximale d’un container YARN : < 65,536 GB , 24 cores >
• Capacité maximale par utilisateur : < 1012,736 GB , 265 cores >
• Application master - capacité maximale : < 304,128 GB , 80 cores >
• Allocation des ressources
– Si le fichier en entrée est de l’ordre de N*256Mo + X (avec X<256Mo)
– Pour exécuter le stage 1 de façon entièrement parallélisé, il faudrait que « num_executors » *
« executor_cores » > N
– Il est recommandé de fixer le nombre de executor_cores à 5, pour ensuite définir le nombre
d’exécuteur à allouer selon la règle précédente. (allouer largement au dessus pour appliquer le
principe de proximité des données).
– Quand il n’y pas de RDD à cacher, 2 Go de RAM par cœurs de processeur est un bon ordre de
grandeur. Donc, il est recommandé d’allouer « exeutor-memory » = 10G.
– Le nombre de partitions à créer à l’issue du shuffle dépend du plan d’exécution et de la
répartition initiale des données, et de l’assignation des tâches effectuée en stage 1.
• Il est recommandé soit de créer autant de partitions que le nombre de cœurs disponibles, et continuer ensuite soit
d’augmenter, soit de diminuer jusqu’à ce que les performances ne continuent plus de s’améliorer
• ou de créer autant de partitions que le nombre de partitions du stage précédent. Continuer ensuite de multiplier ce
nombre par 1,5 jusqu’à ce que les performances s’arrêtent de s’améliorer.
• PARTIE 1 :
• Spark : généralités
• Spark : RDDs et plan d’exécution
• Spark : mode distribué et déploiement sur YARN
• Exemple : Bypass
• PARTIE 2 :
• Spark : PairedRDD
• Spark : tunning des algorithmes et de
l’allocation de ressources
• Annexe : tests de montée en échelle
Sommaire
RDD pairs
Créer un RDD pair (implicite)
Principe de la conversion Implicite :
certaines transformations de RDD ne sont applicables que sur certains types particuliers de RDD. (ex : mean() and variance() sur
les RDD[Double] ou join() sur les RDDs “(clef,valeur)” .
Il s’agit en effet de types différents de RDD, chacun avec sa propre API. En appliquant la transformation groupByKey sur un RDD
contenant des tuples (clef/valeur), il s’agit de la méthode groupByKey de la classe « PairedRDD » qui s’exécute et non pas celle
de la classe « RDD » (car elle n’existe pas dans cette classe). Aucune déclaration explicite n’est nécessaire.
Aussi faudrait il faire attention à ne pas consulter la sclaladoc de la classe RDD pour chercher une transformation qui s’applique
uniquement sur les RDD pairs.
RDDs pairs : transformations
RDDs pairs : agrégations
• ReduceByKey, foldByKey, :
– Les transformations ReduceByKey, et foldByKey prennent en argument une fonction à appliquer successivement à
chaque deux éléments du RDD ayant la même clef.
– Cette fonction doit donc être associative.
• Différence par rapport à groupByKey
– Alors que ces transformations s’exécutent localement sur chaque machine avant d’engendrer un shuffle, la
transformation groupByKey lance directement une opération de shuffle, induisant un trafic important entre les
machines.
• Règle : Si la fonction à appliquer sur un ensemble de valeurs correspondant aux mêmes clefs
est associative, il faut privilégier les transformations ReduceBykey, et foldByKey
• CombineByKey:
– combineByKey(createCombiner, mergeValue,mergeCombiners,partitioner)
RDDs pairs : agrégations
• CombineByKey:
– combineByKey(createCombiner, mergeValue,mergeCombiners,partitioner)
RDDs pairs : parallélisme
• Pour toutes les transformations sur les RDD pairs, il est possible de définir un second
paramètre (degré de parallélisme) qui définit le nombre de partitions du RDD groupé ou
agrégé par clé
Default parallelism « 10 »
Setting parallelism to « 3 »
repartitionnement
RDDs pairs : actions
RDDs pairs : partitionnement
Cas commun
• Considérons une opération de jointure qui s’effectue chaque 5 minutes, entre un RDD
statique et volumineux UserData et un petit RDD events correspondant aux événements
reçus dans les 5 dernières minutes
• Par défault, cette opération effectue un hashing sur l’ensemble des clés des deux RDDs,
envoie ensuite sur le réseaux les éléments ayant la même clef hashé aux même nœuds. La
jointure est ensuite effectuée.
 Les données sont hashés et shufllés à travers le réseau à chaque 5 minutes?
RDDs pairs : partitionnement
Cas commun- solution
• Il est possible au moment de la création du RDD statique userData, de la hash-partitionner.
Attention : Si le RDD n’est pas caché en mémoire, le hash-partitionnement devient inutile
RDDs pairs : partitionnement
Partitionnement par défaut
• Toutes les transformations opérant sur les RDDs pairs automatiquement génèrent un RDD qui est hash-
partitionné
Opérations qui bénéficient du partitionnement
• cogroup(), groupWith(), join(), leftOuterJoin(), rightOuterJoin(), groupByKey(), reduceByKey(), combineByKey(), and lookup()
• Pour les opérations qui s’exécutent sur un seul RDD (reduceByKey), si le RDD et pré-partitionné, il en résulte que l’aggrégration
s’effectue localement dans chaque noeud.
• Pour les opérations qui s’exécutent sur deux RDDs, le pré-partionnement des deux RDDs engendre le shuffle d’un seul des deux RDDs
au maximum.
• S’ils sont persistés dans la même machine, en appelant la fonction “mapValues” par exemple, ou si un des deux RDDs n’as pas encore
été créé, aucun shuffle ne s’exécute dans le réseau
Opérations qui affectent le partitionnement
• toutes les opérations sauf : cogroup(), groupWith(), join(), leftOuterJoin(), rightOuter Join(), groupByKey(), reduceByKey(),
combineByKey(), partitionBy(), sort(), mapValues() (if the parent RDD has a partitioner), flatMapValues() (if parent has a partitioner), and
filter() (if parent has a partitioner)
Tunning
Configurer une application
1. En utilisant sparkConf:
2. Dynamiquement (dans le spark-submit)
3. Spark-default.conf (dans the spark directory par défaut ou dans un répertoire donné)
Paramètres de configuration
Application/Job/Stage/Tâche
• Application : un programme qui peut consister en plusieurs transformations, et actions.
• Job : Quand une application Spark est lancée, chaque action sur une RDD engendre un job
• Stage : Un job contient N+1 stages séquentiels si N est le nombre de transformations de type « Wide ».
• Tâches : Chaque stage se déroule en plusieurs tâches, chacune opérant sur une partition de la RDD en entrée du
stage.
• A l’issue d’un shuffle, le nombre de tâches du stage suivant est défini par le développeur ( ou égal à une valeur par
défaut)
map groupByKey
RDDRDD
Stage 1 Stage 2
Plan d’exécution : exemple
• Jusqu’ici aucune action n’a été appelée. Donc aucun job n’est lancée au sein de l’application.
Aussi, les RDDs sont paresseusement évalués.
• Pour évaluer calculer physiquement les RDDs, il est possible d’appeler une action dessus (collect), qui va donc lancer
un job.
Plan d’exécution : stages
• Le plan d’exécution de ce job est le suivant :
• Une seule transformation de type « wide » => donc deux stages séparés par le shuffle sont générés
• Si le RDD final a été persisté lors d’une première exécution du plan précédent, une nouvelle action « comme
count » par exemple engendrera uniquement un seul stage. S’il n’a pas été persisté, cette action engendrera
un stage supplémentaire en plus des stages précédents.
Plan d’exécution : tâches
• Chaque stage est décliné en plusieurs tâches, opérant chacune et de façon similaire sur une partition du RDD
initial
map
Stage
• Chaque tâche s’exécute de la façon suivante
Parallélisme
• Règle 1 : Chaque stage comprend autant de tâches que de partitions de la RDD en entrée
• Règle 2 : Input RDDs se base directement sur le stockage sous-jacent (bloc hdfs  partition du RDD  tâche)
• Règle 3 : le degré de parallélisme doit être optimal (pas trop petit par rapport aux ressources allouées, et pas
trop grand )
• Règle 4 : Il y a deux façons pour définir son degré de parallélisme (nombre des partitions)
• Degré de parallélisation donné en paramètre aux transformations qui engendrent un shuffle
• En utilisant la fonction repartition(numPartitions) ou coalesce(numPartitions)
• La fonction coalesce(numPartitions) ne fonctionnent que lorsqu’on réduit le nombre de partitions. Elle est
beaucoup plus optimal que la fonction repartition, car elle n’engendre pas de shuffle.
Sérialisation
• Quand Spark transfère les données dans le réseau ou les persistent en disque, il a besoin de sérialiser les
objets (classes) en des formats binaires.
• Spark utilise par défaut le Java’s built in serializer.
• Mais il supporte également l’utilisation d’un serializer tiers appelé « kyro »*
• Sérialisation plus rapide
• Représentation binaire plus compacte
• Attention : les « user-defined » classes doivent implémentée l’interface Java « Serializable ». Si non, une
exception de type « noptSerializableException » sera levée.
Gestion de la mémoire
• Au sein de chaque exécuteur, la mémoire est partagé entre trois fonctionnalités :
Stockage RDD Shuffle et buffers d’agrégation Le code utilisateur
When you call persist() or cache() on an
RDD, its partitions will be stored in
memory buffers.
When performing shuffle operations,
Spark will create intermediate buffers
for storing shuffle output data. These
buffers are used to store intermediate
results of aggregations in addition to
buffering data that is going to be
directly output as part of the shuffle
Memory which is necessary for the
spark code (allocation of arrays for
example)
spark.storage.memoryFraction spark.shuffle.memoryFraction
60% 20% 20%
• Règle : Ces pourcentages doivent être paramétrées en fonction du programme à lancer.
• Règle : MEMORY_AND_DISK storage level est préférable que le MEMORY_ONLY storage level pour la
persistance.
Annexe : tests de montée en
échelle
Tests de performances - Programme de test
• A partir d’une table de CDRs sous forme d’un fichier CSV
stocké dans hdfs, calculer des agrégats (par msisdn)
– Voice total count
– Voice in total count
– SMS in total count
– Calls dispersion
– Cells disperion
– Voice intenational out
Tests de performances - Algorithme spark (scala)
• Lecture du fichiers HDFS
 val data = Sc.textFile(nom_fichier)
• Mapping (séparation des champs)
 val table = data.map(ligne=>ligne.split(‘t’))
• Filtrage (CDR voix in/out et sms in uniquement)
 val types_0_1_6 = table.filter(l=>l(1)==0 || l(1)==1 || l(1)==6)
• Mapping (champs qui nous intéressent uniquement)
 Val ready_table = types_0_1_6 .map( l => (l(4), (l(1),l(5),l(22),l(4)))
• GroupByKey (regroupement par msisdn)
 Val grouped_table= ready_table.groupeByKey()
• Mapping (calcul des compteurs pour chaque msisdn)
 Val compteurs= grouped_table.map( l=> (l._1, fonct_calcul_compteur(l._2)))
• collect(récupération des résultats dans le programme du driver pour restitution stdout)
 For (i<- compteurs.collect()) println(i)
filter groupByKey maptextFile map map take
Tests de performances - Plan d’exécution spark
filter groupByKey maptextFile map map take
filter groupByKey maptextFile map map take
Stage 1 Stage 2
filter groupByKey maptextFile map map take
Stage 1 Stage 2
Fichier HDFS stocké
de façon parallélisée
par blocs de 256 Mo
(ex : 15,4 Go = 60 *
256 Mo + 40 Mo ) RDD : abstraction primaire des
données sous spark correspondant
à une collection distribuée
d’enregistrements dont les
partitions correspondent aux
différents blocs hdfs sous-jacents.
RDD RDD RDDRDD RDD Array
Tests de performances - Plan d’exécution spark
• Supposons que le fichier en entrée est un fichier hdfs text (TextInputFormat)
d’une taille de 15,4 Go = 60 * 256 Mo + 40 Mo
• La lecture du fichier par l’application spark se fait avec la fonction
« textFile() », et permet de constituer un RDD (resilient distributed data).
• Le RDD est composé de 61 partitions :
– Chacune des partitions est physiquement stockée dans un des 61 nœuds contenant les blocs
HDFS du fichier
– Ces nœuds HDFS ne sont pas nécessairement les nœuds exécuteurs alloués pour exécuter les
traitement sur les données.
– Les nœuds exécuteurs chargeront donc ces données en mémoire (ou en disque dur à défaut
de ressources) lorsque le job sera lancé
• Plan d’exécution : L’application comporte plusieurs transformations sur le RDD
et une seule action.
– Un Seul job est donc lancé
– Ce job comporte plusieurs transformation de type Narrow, et une seule transformation de
type « wide »(groupByKey).
– Il comprend donc deux stages séparés par le shuffle
– Le premier stage se déroule sous forme de plusieurs tâches (61 tâches exactement)
– Dans une tâche de ce premier stage, la partition concernée du RDD initial subit toute les
transformations du premier stage.
– Le second stage comprend autant de tâche que le nombre de partitions constitués suite au
shuffle et qui est donné comme paramètre à la fonction « groupByKey »
Tests de performances - Plan d’exécution spark
Jeu de test
• Application du programme de calcul des compteurs
en faisant varier :
– La taille du Fichier CSV de CDRs
– Les ressources allouées
• Pour chaque combinaison (taille de fichier,
ressources allouées), le programme est exécuté plus
de 100 fois, avant de prendre la valeur minimale des
temps d’exécution
• Pour chaque taille du fichier en entrée donnée, la
configuration des ressources qui donne le temps
d’exécution le plus minimal est gardée
Résultats(1)
taille en Go nombre d'executeurs cœurs par excécuteurs mémoire par excuteur (Go) facteur de repartition temps d'excution minimal
0,0045 1 1 2 1 31
0,1 1 2 4 15 38
0,5 1 2 4 2 37
1 5 5 10 25 45
5 5 5 10 30 47
15 30 5 10 150 52
30 25 5 10 120 56
45 38 5 10 400 59
92 110 5 10 359 68
150 90 10 20 1200 73
184 160 5 10 718 85
200 160 5 10 782 84
225 160 5 10 790 87
240 160 5 10 1500 86
270 160 5 10 1500 90
300 160 5 10 1700 91
394 160 5 10 2000 101
2000 180 5 10 5000 247
5000 180 5 10 18000 532
Résultats(2)
20
25
30
35
40
45
50
55
0 2 4 6 8 10 12 14 16
petits volumes
•Même pour le traitement d’un fichier d’une taille de l’ordre de quelques Ko, un temps d’initialisation important est requis (de l’ordre de
30s) pour l’allocation des ressources et l’assignation des tâches.
•Quand la taille du fichier est inférieure à la taille d’un bloc d’un fichier hdsf (256 Mo), une seule tâche est exécutée dans le premier
stage
•Cela ne sert à rien d’allouer plus de deux cœurs (nbr exec * nb cœurs/exec).
•Dans le cas précédent, si on repartitionne suite au shuffle en deux partitions, il faudrait allouer exactement deux cœurs du cluster pour
le job, car deux tâches seront exécutées dans le stage 2
• Dès que la taille du fichier en entrée dépasse 256 Mo, on peut commencer à bénéficier de la parallélisation dans le premier stage.
•Etant donné que le capacité maximale de la queue utilisée est de 790 cœurs, nous pouvons bénéficier de la parallélisation totale lors du
premier stage même avec un fichier d’une taille de 790*256 Mo = 200 Go.
•Ceci explique l’évolution logarithmique constatée à partir de 256 Mo.
Résultats(3)
35
45
55
65
75
85
95
105
0 100 200 300 400 500 600
volumes moyens
•Nous constatons que le temps d’éxécution continue d’évoluer logarithmiquement sur chaque segment de 200 Go.
•Au bout de chaque 200 Go, un saut au niveau du temps d’éxécution est constaté
•Cela est du au fait que sur un segment de 200 Go = 790 * 256 Mo , des cœurs sont toujours disponibles pour traiter d’autres tâches en
parallèle. Dès que les 200 Go sont atteints, la parralélisation est maximale.
•En augmentant ensuite la taille du fichier, il faudrait attendre que les 790 cœurs aient finis leur première tâche, pour en recevoir une
nouvelle. En continuant d’augmenter la taille du fichier de 200 Go, on ne fait que maximiser la parallélisation.
•Dès qu’on dépasse 200 Go à nouveau, un temps d’éxécution supplémentaire est ajouté qui est celui du traitement des blocs
supplémentaires par rapport aux n*790 blocs initiaux.
•Et ainsi de suite …
•Nous remarquons aussi que l’évolution globale peut être approximé par une évolution linéaire
Résultats(4)
90
140
190
240
290
340
390
440
490
540
0 1000 2000 3000 4000 5000 6000
gros volumes
•Dans le ce dernier graphique, nous ne prenons plus des intervalles de 200 Go.
•On constate bien que l’évolution globale du temps d’exécution constatés pour les trois fichiers testés en entrée, qui sont de 500 Go,
2To, 5To, est bien linéaire.
•Si nous testons plusieurs fichiers au sein de chaque segment de 200 Go, nous aurions bien constaté une évolution locale
logarithmique sur chaque segment, en plus de l’évolution linéaire globale constatée

Contenu connexe

Tendances

Tendances (20)

BigData_Chp2: Hadoop & Map-Reduce
BigData_Chp2: Hadoop & Map-ReduceBigData_Chp2: Hadoop & Map-Reduce
BigData_Chp2: Hadoop & Map-Reduce
 
BigData_TP3 : Spark
BigData_TP3 : SparkBigData_TP3 : Spark
BigData_TP3 : Spark
 
Introduction NoSql 201406 - lbroudoux
Introduction NoSql 201406 - lbroudouxIntroduction NoSql 201406 - lbroudoux
Introduction NoSql 201406 - lbroudoux
 
Spark (v1.3) - Présentation (Français)
Spark (v1.3) - Présentation (Français)Spark (v1.3) - Présentation (Français)
Spark (v1.3) - Présentation (Français)
 
TP1 Big Data - MapReduce
TP1 Big Data - MapReduceTP1 Big Data - MapReduce
TP1 Big Data - MapReduce
 
Cours Big Data Chap6
Cours Big Data Chap6Cours Big Data Chap6
Cours Big Data Chap6
 
BigData_TP2: Design Patterns dans Hadoop
BigData_TP2: Design Patterns dans HadoopBigData_TP2: Design Patterns dans Hadoop
BigData_TP2: Design Patterns dans Hadoop
 
Les Base de Données NOSQL -Presentation -
Les Base de Données NOSQL -Presentation -Les Base de Données NOSQL -Presentation -
Les Base de Données NOSQL -Presentation -
 
Cours Big Data Chap2
Cours Big Data Chap2Cours Big Data Chap2
Cours Big Data Chap2
 
Spark RDD : Transformations & Actions
Spark RDD : Transformations & ActionsSpark RDD : Transformations & Actions
Spark RDD : Transformations & Actions
 
Base de données graphe, Noe4j concepts et mise en oeuvre
Base de données graphe, Noe4j concepts et mise en oeuvreBase de données graphe, Noe4j concepts et mise en oeuvre
Base de données graphe, Noe4j concepts et mise en oeuvre
 
Présentation des bases de données NoSql
Présentation des bases de données NoSqlPrésentation des bases de données NoSql
Présentation des bases de données NoSql
 
Chapitre 2 hadoop
Chapitre 2 hadoopChapitre 2 hadoop
Chapitre 2 hadoop
 
Cours Big Data Chap1
Cours Big Data Chap1Cours Big Data Chap1
Cours Big Data Chap1
 
MongoDB.pptx
MongoDB.pptxMongoDB.pptx
MongoDB.pptx
 
BigData_Chp1: Introduction à la Big Data
BigData_Chp1: Introduction à la Big DataBigData_Chp1: Introduction à la Big Data
BigData_Chp1: Introduction à la Big Data
 
Les BD NoSQL
Les BD NoSQLLes BD NoSQL
Les BD NoSQL
 
BigData_Chp4: NOSQL
BigData_Chp4: NOSQLBigData_Chp4: NOSQL
BigData_Chp4: NOSQL
 
Les Base de Données NOSQL
Les Base de Données NOSQLLes Base de Données NOSQL
Les Base de Données NOSQL
 
Technologies pour le Big Data
Technologies pour le Big DataTechnologies pour le Big Data
Technologies pour le Big Data
 

En vedette

En vedette (20)

Spark, ou comment traiter des données à la vitesse de l'éclair
Spark, ou comment traiter des données à la vitesse de l'éclairSpark, ou comment traiter des données à la vitesse de l'éclair
Spark, ou comment traiter des données à la vitesse de l'éclair
 
Introduction spark
Introduction sparkIntroduction spark
Introduction spark
 
Apache SPARK ML : principes, concepts et mise en œuvre
Apache SPARK  ML : principes, concepts et  mise en œuvre Apache SPARK  ML : principes, concepts et  mise en œuvre
Apache SPARK ML : principes, concepts et mise en œuvre
 
Scala: Pattern matching, Concepts and Implementations
Scala: Pattern matching, Concepts and ImplementationsScala: Pattern matching, Concepts and Implementations
Scala: Pattern matching, Concepts and Implementations
 
Scala : programmation fonctionnelle
Scala : programmation fonctionnelleScala : programmation fonctionnelle
Scala : programmation fonctionnelle
 
Spark dataframe
Spark dataframeSpark dataframe
Spark dataframe
 
Spark une alternative à Hadoop MapReduce pour le Datamining
Spark une alternative à Hadoop MapReduce pour le DataminingSpark une alternative à Hadoop MapReduce pour le Datamining
Spark une alternative à Hadoop MapReduce pour le Datamining
 
Lambda architecture et Spark
Lambda architecture et SparkLambda architecture et Spark
Lambda architecture et Spark
 
0712_Seigneurin
0712_Seigneurin0712_Seigneurin
0712_Seigneurin
 
Hadoop and Voldemort @ LinkedIn
Hadoop and Voldemort @ LinkedInHadoop and Voldemort @ LinkedIn
Hadoop and Voldemort @ LinkedIn
 
Interface fonctionnelle, Lambda expression, méthode par défaut, référence de...
Interface fonctionnelle, Lambda expression, méthode par défaut,  référence de...Interface fonctionnelle, Lambda expression, méthode par défaut,  référence de...
Interface fonctionnelle, Lambda expression, méthode par défaut, référence de...
 
Cartographie du big data
Cartographie du big dataCartographie du big data
Cartographie du big data
 
Casablanca Hadoop & Big Data Meetup - Introduction à Hadoop
Casablanca Hadoop & Big Data Meetup - Introduction à HadoopCasablanca Hadoop & Big Data Meetup - Introduction à Hadoop
Casablanca Hadoop & Big Data Meetup - Introduction à Hadoop
 
Lessons Learned: Using Spark and Microservices
Lessons Learned: Using Spark and MicroservicesLessons Learned: Using Spark and Microservices
Lessons Learned: Using Spark and Microservices
 
Présentation de Apache Zookeeper
Présentation de Apache ZookeeperPrésentation de Apache Zookeeper
Présentation de Apache Zookeeper
 
BigData_TP1: Initiation à Hadoop et Map-Reduce
BigData_TP1: Initiation à Hadoop et Map-ReduceBigData_TP1: Initiation à Hadoop et Map-Reduce
BigData_TP1: Initiation à Hadoop et Map-Reduce
 
Software Engineering - chp8- deployment
Software Engineering - chp8- deploymentSoftware Engineering - chp8- deployment
Software Engineering - chp8- deployment
 
Jay Kreps on Project Voldemort Scaling Simple Storage At LinkedIn
Jay Kreps on Project Voldemort Scaling Simple Storage At LinkedInJay Kreps on Project Voldemort Scaling Simple Storage At LinkedIn
Jay Kreps on Project Voldemort Scaling Simple Storage At LinkedIn
 
Hadoop et son écosystème
Hadoop et son écosystèmeHadoop et son écosystème
Hadoop et son écosystème
 
Realtime Web avec Akka, Kafka, Spark et Mesos - Devoxx Paris 2014
Realtime Web avec Akka, Kafka, Spark et Mesos - Devoxx Paris 2014Realtime Web avec Akka, Kafka, Spark et Mesos - Devoxx Paris 2014
Realtime Web avec Akka, Kafka, Spark et Mesos - Devoxx Paris 2014
 

Similaire à spark_intro_1208

Hadoop and friends : introduction
Hadoop and friends : introductionHadoop and friends : introduction
Hadoop and friends : introduction
fredcons
 
0251-formation-java-programmation-objet.pdf
0251-formation-java-programmation-objet.pdf0251-formation-java-programmation-objet.pdf
0251-formation-java-programmation-objet.pdf
Ombotimbe Salifou
 

Similaire à spark_intro_1208 (20)

Distributed computing with Spark 2.x
Distributed computing with Spark 2.xDistributed computing with Spark 2.x
Distributed computing with Spark 2.x
 
Spark
SparkSpark
Spark
 
160813-technology-template-16x9.pptx
160813-technology-template-16x9.pptx160813-technology-template-16x9.pptx
160813-technology-template-16x9.pptx
 
Tech day hadoop, Spark
Tech day hadoop, SparkTech day hadoop, Spark
Tech day hadoop, Spark
 
Techday Arrow Group: Hadoop & le Big Data
Techday Arrow Group: Hadoop & le Big DataTechday Arrow Group: Hadoop & le Big Data
Techday Arrow Group: Hadoop & le Big Data
 
Infrastructure as code drupal
Infrastructure as code drupalInfrastructure as code drupal
Infrastructure as code drupal
 
Hadoop Introduction in Paris
Hadoop Introduction in ParisHadoop Introduction in Paris
Hadoop Introduction in Paris
 
ch2-hadoop-L3-2023-4p (1).pdf
ch2-hadoop-L3-2023-4p (1).pdfch2-hadoop-L3-2023-4p (1).pdf
ch2-hadoop-L3-2023-4p (1).pdf
 
Spark docker
Spark dockerSpark docker
Spark docker
 
Hadoop and friends : introduction
Hadoop and friends : introductionHadoop and friends : introduction
Hadoop and friends : introduction
 
Introduction Apche Spark
Introduction Apche SparkIntroduction Apche Spark
Introduction Apche Spark
 
REX Ansible
REX AnsibleREX Ansible
REX Ansible
 
Drupal et le NoSQL - drupagora 2011
Drupal et le NoSQL - drupagora 2011Drupal et le NoSQL - drupagora 2011
Drupal et le NoSQL - drupagora 2011
 
Presentation intis 2017 version27112017
Presentation intis 2017 version27112017Presentation intis 2017 version27112017
Presentation intis 2017 version27112017
 
Docker nice meetup #1 construire, déployer et exécuter vos applications, ...
Docker nice meetup #1   construire, déployer et exécuter vos applications, ...Docker nice meetup #1   construire, déployer et exécuter vos applications, ...
Docker nice meetup #1 construire, déployer et exécuter vos applications, ...
 
PerfUG - Hadoop Performances
PerfUG - Hadoop PerformancesPerfUG - Hadoop Performances
PerfUG - Hadoop Performances
 
Architecture Big Data open source S.M.A.C.K
Architecture Big Data open source S.M.A.C.KArchitecture Big Data open source S.M.A.C.K
Architecture Big Data open source S.M.A.C.K
 
Spark - An In-Memory Distributed Computing Engine.pptx
Spark - An In-Memory Distributed Computing Engine.pptxSpark - An In-Memory Distributed Computing Engine.pptx
Spark - An In-Memory Distributed Computing Engine.pptx
 
Utilisation de dkms
Utilisation de dkmsUtilisation de dkms
Utilisation de dkms
 
0251-formation-java-programmation-objet.pdf
0251-formation-java-programmation-objet.pdf0251-formation-java-programmation-objet.pdf
0251-formation-java-programmation-objet.pdf
 

spark_intro_1208

  • 3. A propos de spark • Spark est une plateforme de traitement distribué désignée pour être : – rapide : in-memory, shell interactif, traitement itératif – « general-purpose » : un seul cœur (Spark) exposant des APIs pour manipuler des collections distribuées de données (RDD) • Spark et hadoop : – Spark peut être installé dans un cluster hadoop (mais pas nécessairement) – APIs haut niveau – Spark remédie à plusieurs défauts de Mapreduce
  • 4. Spark Stack Spark core : • fonctionnalités basiques de spark : scheduling des tâches, gestion de mémoire, fault recovery, interaction avec le système de stockage • fournit plusieurs API de définition et manipulation des RDD : abstraction primaire des donnés sous forme de collection distribuée d'items
  • 5. Spark Stack Spark SQL: • interface SQL qui s'intègre avec le spark core dans les mêmes applications : • manipulation SQL des données structurées + analyse avancées des rdd avec scala python ou java
  • 6. Spark Stack Spark MLIB: • Classification, régression, clustering, collaborative filtering • évaluation des modèles • des primitives bas niveau : « gradient descent algo » • toutes désignées pour le « scaling out » a travers plusieurs noeuds
  • 7. Spark Stack Spark Streaming: • composant spark pour traiter les flux live de données : logfiles .. l'API streaming est proche de celle de spark’s core RDD API. Ceci facilite la transition pour un développeur qui programme des traitements sur des données en mémoire ou en disque , à des traitement en temps réel
  • 8. Spark Stack Spark Graphx: • bibliothèque pour manipuler des graphes , étend l’ API spark RDD comme streaming et SQL
  • 9. Cluster managers • Spark peut continuer de « scaler » a travers un cluster de plusieurs milliers de nœuds. • Il supporte plusieurs clusters managers : – Hadoop yarn – Apache mesos – Standalone scheduler : empty set of machines – local : tout les processus spark dans un seul thread Spark Stack
  • 10. Stockage • Spark peut utiliser tous les systèmes de stockage supportés par l’API de Hadoop: – HDFS – S3 – Local FS – Cassandra – Hive – Hbase • INPUT et OUTPUT Format – Spark peut accéder en lecture ou en écriture à tous les formats qui utilisent les interfaces InputFormat et OutputFormat utilisées par Hadoop Mapreduce
  • 11. Architecture maître-esclave • Driver program: – Exécution du main de la classe codée par le développeur – Initialise un « spark context » : connexion à un « computing cluster » • Spark Executors : – Des nœuds esclaves gérés par le driver pour la durée de vie d’un « spark context » – se chargent d’exécuter des tâches en parallèle
  • 12. Deux modes d’utilisation – shell interactif • Spark-shell
  • 13. Deux modes d’utilisation – standalone applications • Scala project – Fraud.scala • Compilation and versionning : sbt tool – Build.sbt name := « Fraud_Bypass" version := "0.0.1" scalaVersion := "2.10.4 " libraryDependencies ++="org.apache.spark" %% "spark-core" % "1.2.1" – Linux shell >> sbt package • Compilation – Linux Shell >> spark-submit –class Fraud Fraud_Bypass-0.0.1.jar arg_to_the_main_1 arg_to_the_main_2 …….
  • 14. Spark : RDDs et plan d’exécution
  • 15. RDDs • Un RDD est l’abstraction primaire des données sous SPARK • Un RDD correspond à une collection non mutable distribuée d’objets, qui peuvent être de n’importe quel type de données Python, Scala ou Java, ou des “user-defined class” • Chaque RDD est constitué de plusieurs partitions distribuées sur le cluster. • Spark distribue automatiquement les RDDs selon le nombre de partitions souhaitées (ou selon un nombre par défaut) • Les RDD sont “fault-tolerant”, et bénéficie d’un mécanisme de reconstitution en cas de perte d’une partition donnée • Il est possible de créer un RDD :  En chargeant un collection externe de données par le driver programme (depuis hdfs par exemple)  Ou en créant un nouveau RDD en appliquant une transformation sur un ancien RDD  Ou en distribuant une collection d’objets (List, Set..) dans le driver programme
  • 16. RDDs opérations • Il existe deux types d’opérations qu’on peut appliquer sur un RDD – Les transformations : permettent de constituer un nouveau RDD (map– filter – groupByKey – join) – Les actions : permettent de créer un résultat à partir d’un RDD pour le retourner au driver ou l’écrire en système de stockage. (count – collect – saveAsTextFile) • Example : – A partir d’un RDD de CDRs, une transformation de filtrage permet de créer un nouveau RDD contenant uniquement les types de « 0 » – Une fois créée, on applique une action « take(3) » sur le RDD, pour récupérer au niveau du driver un tableau de 3 CDRs
  • 17. RDDs opérations - transformations
  • 18. RDDs operations - transformations LAZY COMPUTING • Principe : – Au moment de la création d’un nouveau RDD, Spark se contente de garder en mémoire l’ensemble des étapes nécessaires pour calculer le RDD. – Il ne le crée physiquement qu’on moment ou il doit être utilisé dans une action • Avantages : – Gestion efficace du disque et de la mémoire – Optimisation des données à charger ( Exemple de succession des transformations : textFile + filter) – Optimisation des traitements à effectuer pour exécuter une succession de transformations en les regroupant (exemple d’un succession de d’opération de mapping) – Corolaire du dernier point : Code plus léger, moins compact, et facilement compréhensible (par rapport à map reduce) • Inconvénient : – Si un RDD doit être utilisé dans n actions au sein d’une même application, il sera calculé n fois différentes – Solution : Il est possible de persister un RDD en mémoire (ou en disque). Celui-ci sera calculé une seule fois, lors de l’exécution de la première action qui utilise ce RDD, et sera gardé en mémoire de façon distribuée au niveau de tous les exécuteurs utilisés pour le calculer. Lors de l’éxécution d’une nouvelle action qui utilise ce RDD, celui est accessible rapidement en mémoire, et ne sera pas recalculé. // spark ne charge pas physiquement les données du RDD // spark mémorise comment calculer second_RDD // spark mémorise comment calculer Third_RDD // spark charge physiquement les données du RDD pour établir //first_RDD, calcule physiquement « second_RDD » et « third_RDD » // Il effectue ensuite l’action de comptage, dont la valeur retour est //retrounée au driver
  • 19. Persistance • Scala java – par défault : Stockage en mémoire sous forme d’objets non serialisés • Python – par défault : Stockage en mémoire sous forme d’objets sérialisés • Suite à une défaillance d’un noeud, Spark re-calcule les partitions perdues au besoin. • Il est possible de répliquer les données persistées. • Si les données à cacher dépassent les capacités de mémoire, Spark supprime les anciennes partitions persistées. – En mémoire : il re-calcule les parititions supprimées au besoin – En mémoire-et-disque : Il les écrira en disque • Pour libérer la mémoire, on peut appeller la fonction “unpersist()” RDDs opérations - transformations
  • 20. Deux types de transformations Il existe deux types de transformations sur les RDDs • Narrow : tous les enregistrement dans une des partitions du RDD fils , sont calculés uniquement à partir des enregistrements d’une et une seule partition du RDD parent • Wide : La constitution d’une partition fait intervenir plusieurs partitions  Le shuffle qui intervient dans toutes les transformations de type « wide » est une opération très consommatrice en bande passante au sein du cluster et peut créer un goulot d’étranglement.  Règle pratique : Il est important d’effectuer toutes les opérations de filtrage avant les transformations de type « wide », afin d’avoir le minimum possible de données à « shuffler » entre les noeuds RDDs opérations - transformations
  • 21. RDDs opérations – basic actions - Toutes ces opérations retournent des résultats au driver (sauf foreach() qui est exécuté directement au niveau des « executors » )  ATTENTION : le retour de la fonction « collect » doit être supporté par une seule machine (celle du driver), et peut éventuellement causer l’echec du job
  • 22. Plan d’exécution d’une application spark Vocabulaire : Application/Job/Stage/Tâche • Application : un programme qui peut consister en plusieurs transformations, et actions. • Job : Quand une application Spark est lancée, chaque action sur une RDD engendre un job • Stage : Un job contient N+1 stages séquentiels si N est le nombre de transformations de type « Wide ». • Tâches : Chaque stage se déroule en plusieurs tâches, chacune opérant sur une partition de la RDD en entrée du stage. • A l’issue d’un shuffle, le nombre de tâches du stage suivant est défini par le développeur ( ou égal à une valeur par défaut) map groupByKey RDDRDD Stage 1 Stage 2
  • 23. Spark : mode distribué et YARN
  • 24. Mode distribué • Composants d’une application spark – Spark driver : s’exécute dans son propre process java. – Spark executors : chacun s’exécute dans son propre java process • La négociation des ressources se fait entre le driver et le cluster manager (Yarn, Mesos, ..)
  • 25. Le driver - Le driver est un process java , dans lequel s’exécute le main du programme codé par le développeur. - Il exécute deux fonctions principales : • Convertir un programme en un plan d’exécution – À partir d’un graphe acyclique logique qui caractérise le programme … – ...le driver établit un plan d’exécution physique sous forme de plusieurs stages, contenant plusieurs tâches à faire exécuter par le cluster • Ordonnancer l’exécution du plan d’exécution – Les exécuteurs s’enregistrent auprès du driver – Le driver se charge ensuite d’assigner les différentes tâches aux exécuteurs de façon à maximiser le principe de proximité des données – Le driver mémorise également les endroits de persistance des différentes partitions des RDDs (quand elles sont persistées), afin d’optimiser l’assignation des futures tâches qui accéderont à ces données
  • 26. Les exécuteurs • Un exécuteur spark est un process Java qui s’occupe de l’exécution des tâches que lui assigne le driver. Il est lancé au début de l’application et reste disponible pendant toute la durée du job. Si un exécuteur défaille, l’application continue d’être exécutée et un mécanisme de récupération de données et/ou de tâches est géré par Spark de façon transparente. • Rappel : Un process java (ici une JVM) peut lancer parallèlement un nombre maximal de threads (tâches), qui est égal au nombre de cœurs de processeurs alloués pour le process.  Il remplit deux fonctions : – Exécuter les tâches qui leurs sont assignées et retournent des résultats au driver – Offrir du stockage en mémoire aux RDDs persistées en mémoire
  • 27. Lancement d’un programme 1. L’utilisateur soumet une application spark par le biais de la commande spark-submit 2. Le programme driver est lancé dans un process java , et invoque la métode main du programme lancé par l’utisateur 3. Le programme driver contacte le cluster manager pour allouer des ressources pour lancer des exécuteurs, coformément aux ressources demandées par l’utilisateur 4. Le cluster manager lance les exécteurs pour le compte du driver 5. Le driver consitute le plan d’exécution et assigne des tâches élémentaires aux exécuteurs 6. Le driver se termine quand le main() finit de s’exécuter ou suite à un appel de SparkContext.stop(). 7. Il arrête les éxécuteurs et libère les ressources auprès du cluster manager.
  • 28. Le spark-submit • Le format général :  bin/spark-submit [options] <app jar | python file> [app options] • Les options
  • 30. Spark sur Yarn : architecture Mode Yarn-client Lorsque Yarn est utilisé comme Cluster Manager, il faudrait bien distinguer entre les deamons YARN et les deamons SPARK YARN  RM : responsable d’affectation des ressources  Spark AM : demande les ressources auprès du RM  Spark NM : nœuds contenant un ou plus de container, chacun pouvant accueillir un exécuteur spark. SPARK  Spark Driver : s’exécute chez le client en mode Yarn-client , ou dans l’application master en mode Yarn- cluster  Spark Executor : s’exécutent dans des containers YARN au sein du node manager. ATTENTION : deux exécuteurs spark peuvent se retrouver dans deux containers Yarn différents, mais au sein de la même machine (YARN node manager)
  • 31. Spark sur Yarn– Application/Job/Stage/Tâche • Quand une application Spark est lancée, chaque action sur une RDD engendre un job qui contient N+1 stages séquentiels si N est le nombre de transformations de type « Wide ». • Chaque stage se déroule en plusieurs tâches, chacune opérant sur une partition de la RDD précédente. • Au sein d’un stage(où toutes les transformations sont de type « narrow »), le nombre de tâches est égal exactement au nombre de partitions du dernier RDD constitué, qui lui-même est (supérieur ou) égal au nombre de partitions du RDD initial (nombre de blocs hdfs sous-jacents). • A l’issue d’un shuffle, le nombre de tâches du stage suivant est défini par le développeur ( ou égal à 1 par défaut) • Une tâche est exécutée par un cœur processeur d’un exécuteur spark (container Yarn) alloué pour l’application. • Les tâches d’un stage se déroulent parallèlement si les ressources allouées le permettent. • Deux stages ou jobs indépendants peuvent être exécutés parallèlement.  Règle : Afin de paralléliser entièrement les tâches d’un stage contenant des transformations de type « Narrow », le nombre d’exécuteurs alloués * le nombre de cœurs par exécuteur DOIT ETRE SUPERIEUR OU EGAL au nombre de blocs HDFS du fichier initial  Règle : En général, Le nombre d’exécuteur alloués * le nombre de cœurs par exécuteur DOIT ETRE SUPERIEUR OU EGAL au nombre de tâches du stage (ou des stages si ceux-ci sont indépendants et donc parallèles) contenant le plus grand nombre de tâches.  Règle : Si les ressources le permettent, il est préférable d’en allouer largement au dessus des seuils précédents, afin d’avoir plus de chances d’appliquer le principe de proximité des données (chaque partition est traitée par un exécuteur lancé dans le même nœud hdfs contenant le bloc correspondant) map groupByKey RDDRDD Stage 1 Stage 2
  • 33. Objectif • A partir d’une table de CDRs sous forme d’un fichier CSV stocké dans hdfs, calculer des agrégats (par msisdn) – Voice total count – Voice in total count – SMS in total count – Calls dispersion – Cells disperion – Voice intenational out • Comparer ses compteurs à des valeurs seuils, pour filtrer les comportements fraudeurs
  • 34. 1. Read CDR decoded Data  Val data = sc.textFile(« path/to/files ») 2. Constructing a paired RDD  Val paired_data = filtered_data.map( row=> ( row(msisdn) , CDR_Schema_Apply(row) ) 3. Grouping_by_key  Val grouped_by_key_data= paired_data.groupByKey(numOfPartitions) 4. Applying the rule()  Val fraud_list = grouped_by_key_data.filter( (key,value) => rule_func(value, json) ) 5. The driver collects the results in an array of Strings , (before writing back to a Hive (or Hbase table))  Val result= fraud_list .collect() Algorithme spark (code sous-jacent en scala) DAG
  • 35. Algorithme spark : Object_func  rule_funct( rule : Rule, cdrs : Iterable[Cdr] ) : Boolean Return condition_func(rule.condition(1), cdrs) rule.binaryOperators(1) condition_funct(rule.condition(2),cdrs) rule.binaryOperators(2) condition_funct(rule.condition(3),cdrs) …….. condition_func (condition : Condition , cdrs : Iterable[Cdr] ) : Boolean Condition.comparison_op match { case « ge » => return operation_funct( condition.operations(1) , cdrs) >= operation_funct( condition.operations(2) ,cdrs) case « le » => return operation_funct( condition.operations(1) , cdrs) <= operation_funct( condition.operations(2) ,cdrs) case « g » => return operation_funct( condition.operations(1) , cdrs) > operation_funct( condition.operations(2) , cdrs) case « l » => return operation_funct( condition.operations(1) , cdrs) < operation_funct( condition.operations(2) , cdrs) case « eq » => return operation_funct( condition.operations(1) , cdrs) == operation_funct(condition.operations(2) , cdrs) case « not » => return operation_funct( condition.operations(1) , cdrs) == 0 } operation_func(Operation : operation , cdrs : Iterable[Cdr] ) : Float Operation.operation_type match { case « ratio » => return Function_funct( operations.functions(1) , cdrs) / Function_funct( operations.functions(2) , cdrs) case « product » => return Function_funct( operations.functions(1) , cdrs) *Function_funct( operations.functions(2) , cdrs) case « filter » | « static_threshlod » | « counter » => return Function_funct( operations.functions(1) , cdrs) function_funct (function : Function , cdrs : Iterable[Cdr] ) : Float Function.function_type match { case « count » => return cdrs.count(cdr => function.fields.contains(cdr.recordType)) case « static_value » => return function.fields(0).toFloat case « filter » => return sc.textFile(function.fields(0)).collect().contains(cdr.msisdn) case « disitinct_count » => countList
  • 36. map groupByKeytextFile Stage 1 Stage 2 HDFS csv file (or hive table) of n blocks data : RDD [String] : primary data abstraction in Spark. It contains a distributed set of lines (rows) Grouped_by_key_data : Paired_RDD [(key : String ,value : Iterable[Tuple [String]])] paired_data : Paired_RDD [ (key : String , value : Tuple[String]) ] Result : Array [Strings] Fraud_list: Paired_RDD [(key : String ,value : Iterable[Tuple [String]])] collect (*) : source : Learning spark - O'Reilly Media filter  4 blocks => 4 Tasks in stage 1 num_executors * executors_cores > N !!!!! numPartions given as in input to groupByKey_ function=> numPartions Tasks in stage 2 numOfPari tions > queue_total _cores (=790 for « HQ_IST » queue » !!! (*) Plan d’exécution • Supposons que le fichier en entrée est d’une taille de 1Go = 3 * 256Mo + 232Mo (4 blocs hdfs) • L’application comporte plusieurs transformations sur le RDD et une seule action. – Un Seul job est donc lancé – Ce job comporte plusieurs transformations de type Narrow, et une seule transformation de type wide(groupByKey). – Il comprend donc deux stages séparés par le shuffle – Le premier stage se déroule sous forme de plusieurs tâches (4 tâches exactement) – Dans une tâche de ce premier stage, la partition concernée du RDD initial subit toute les transformations du premier stage. – Le second stage comprend un nombre de tâches exactement égal à la valeur de repartitionnement donnée en paramètre à la fonction groupByKey (3 partitions dans l’illustration ci-dessous)
  • 37. Allocation des ressources pour le programme • Rappel : capacités du cluster de la PEI : – 2 racks, 55 Nœuds actifs, <memory: 4.83 TB , vCores : 1320> • Deux types de nœuds : < 58.88 GB , 24 cores > ou < 121.99 GB , 24 cores > – Capacités maximales pour la queue « HQ_IST » • Capacité maximale d’un container YARN : < 65,536 GB , 24 cores > • Capacité maximale par utilisateur : < 1012,736 GB , 265 cores > • Application master - capacité maximale : < 304,128 GB , 80 cores > • Allocation des ressources – Si le fichier en entrée est de l’ordre de N*256Mo + X (avec X<256Mo) – Pour exécuter le stage 1 de façon entièrement parallélisé, il faudrait que « num_executors » * « executor_cores » > N – Il est recommandé de fixer le nombre de executor_cores à 5, pour ensuite définir le nombre d’exécuteur à allouer selon la règle précédente. (allouer largement au dessus pour appliquer le principe de proximité des données). – Quand il n’y pas de RDD à cacher, 2 Go de RAM par cœurs de processeur est un bon ordre de grandeur. Donc, il est recommandé d’allouer « exeutor-memory » = 10G. – Le nombre de partitions à créer à l’issue du shuffle dépend du plan d’exécution et de la répartition initiale des données, et de l’assignation des tâches effectuée en stage 1. • Il est recommandé soit de créer autant de partitions que le nombre de cœurs disponibles, et continuer ensuite soit d’augmenter, soit de diminuer jusqu’à ce que les performances ne continuent plus de s’améliorer • ou de créer autant de partitions que le nombre de partitions du stage précédent. Continuer ensuite de multiplier ce nombre par 1,5 jusqu’à ce que les performances s’arrêtent de s’améliorer.
  • 38. • PARTIE 1 : • Spark : généralités • Spark : RDDs et plan d’exécution • Spark : mode distribué et déploiement sur YARN • Exemple : Bypass • PARTIE 2 : • Spark : PairedRDD • Spark : tunning des algorithmes et de l’allocation de ressources • Annexe : tests de montée en échelle Sommaire
  • 40. Créer un RDD pair (implicite) Principe de la conversion Implicite : certaines transformations de RDD ne sont applicables que sur certains types particuliers de RDD. (ex : mean() and variance() sur les RDD[Double] ou join() sur les RDDs “(clef,valeur)” . Il s’agit en effet de types différents de RDD, chacun avec sa propre API. En appliquant la transformation groupByKey sur un RDD contenant des tuples (clef/valeur), il s’agit de la méthode groupByKey de la classe « PairedRDD » qui s’exécute et non pas celle de la classe « RDD » (car elle n’existe pas dans cette classe). Aucune déclaration explicite n’est nécessaire. Aussi faudrait il faire attention à ne pas consulter la sclaladoc de la classe RDD pour chercher une transformation qui s’applique uniquement sur les RDD pairs.
  • 41. RDDs pairs : transformations
  • 42. RDDs pairs : agrégations • ReduceByKey, foldByKey, : – Les transformations ReduceByKey, et foldByKey prennent en argument une fonction à appliquer successivement à chaque deux éléments du RDD ayant la même clef. – Cette fonction doit donc être associative. • Différence par rapport à groupByKey – Alors que ces transformations s’exécutent localement sur chaque machine avant d’engendrer un shuffle, la transformation groupByKey lance directement une opération de shuffle, induisant un trafic important entre les machines. • Règle : Si la fonction à appliquer sur un ensemble de valeurs correspondant aux mêmes clefs est associative, il faut privilégier les transformations ReduceBykey, et foldByKey • CombineByKey: – combineByKey(createCombiner, mergeValue,mergeCombiners,partitioner)
  • 43. RDDs pairs : agrégations • CombineByKey: – combineByKey(createCombiner, mergeValue,mergeCombiners,partitioner)
  • 44. RDDs pairs : parallélisme • Pour toutes les transformations sur les RDD pairs, il est possible de définir un second paramètre (degré de parallélisme) qui définit le nombre de partitions du RDD groupé ou agrégé par clé Default parallelism « 10 » Setting parallelism to « 3 » repartitionnement
  • 45. RDDs pairs : actions
  • 46. RDDs pairs : partitionnement Cas commun • Considérons une opération de jointure qui s’effectue chaque 5 minutes, entre un RDD statique et volumineux UserData et un petit RDD events correspondant aux événements reçus dans les 5 dernières minutes • Par défault, cette opération effectue un hashing sur l’ensemble des clés des deux RDDs, envoie ensuite sur le réseaux les éléments ayant la même clef hashé aux même nœuds. La jointure est ensuite effectuée.  Les données sont hashés et shufllés à travers le réseau à chaque 5 minutes?
  • 47. RDDs pairs : partitionnement Cas commun- solution • Il est possible au moment de la création du RDD statique userData, de la hash-partitionner. Attention : Si le RDD n’est pas caché en mémoire, le hash-partitionnement devient inutile
  • 48. RDDs pairs : partitionnement Partitionnement par défaut • Toutes les transformations opérant sur les RDDs pairs automatiquement génèrent un RDD qui est hash- partitionné Opérations qui bénéficient du partitionnement • cogroup(), groupWith(), join(), leftOuterJoin(), rightOuterJoin(), groupByKey(), reduceByKey(), combineByKey(), and lookup() • Pour les opérations qui s’exécutent sur un seul RDD (reduceByKey), si le RDD et pré-partitionné, il en résulte que l’aggrégration s’effectue localement dans chaque noeud. • Pour les opérations qui s’exécutent sur deux RDDs, le pré-partionnement des deux RDDs engendre le shuffle d’un seul des deux RDDs au maximum. • S’ils sont persistés dans la même machine, en appelant la fonction “mapValues” par exemple, ou si un des deux RDDs n’as pas encore été créé, aucun shuffle ne s’exécute dans le réseau Opérations qui affectent le partitionnement • toutes les opérations sauf : cogroup(), groupWith(), join(), leftOuterJoin(), rightOuter Join(), groupByKey(), reduceByKey(), combineByKey(), partitionBy(), sort(), mapValues() (if the parent RDD has a partitioner), flatMapValues() (if parent has a partitioner), and filter() (if parent has a partitioner)
  • 50. Configurer une application 1. En utilisant sparkConf: 2. Dynamiquement (dans le spark-submit) 3. Spark-default.conf (dans the spark directory par défaut ou dans un répertoire donné)
  • 52. Application/Job/Stage/Tâche • Application : un programme qui peut consister en plusieurs transformations, et actions. • Job : Quand une application Spark est lancée, chaque action sur une RDD engendre un job • Stage : Un job contient N+1 stages séquentiels si N est le nombre de transformations de type « Wide ». • Tâches : Chaque stage se déroule en plusieurs tâches, chacune opérant sur une partition de la RDD en entrée du stage. • A l’issue d’un shuffle, le nombre de tâches du stage suivant est défini par le développeur ( ou égal à une valeur par défaut) map groupByKey RDDRDD Stage 1 Stage 2
  • 53. Plan d’exécution : exemple • Jusqu’ici aucune action n’a été appelée. Donc aucun job n’est lancée au sein de l’application. Aussi, les RDDs sont paresseusement évalués. • Pour évaluer calculer physiquement les RDDs, il est possible d’appeler une action dessus (collect), qui va donc lancer un job.
  • 54. Plan d’exécution : stages • Le plan d’exécution de ce job est le suivant : • Une seule transformation de type « wide » => donc deux stages séparés par le shuffle sont générés • Si le RDD final a été persisté lors d’une première exécution du plan précédent, une nouvelle action « comme count » par exemple engendrera uniquement un seul stage. S’il n’a pas été persisté, cette action engendrera un stage supplémentaire en plus des stages précédents.
  • 55. Plan d’exécution : tâches • Chaque stage est décliné en plusieurs tâches, opérant chacune et de façon similaire sur une partition du RDD initial map Stage • Chaque tâche s’exécute de la façon suivante
  • 56. Parallélisme • Règle 1 : Chaque stage comprend autant de tâches que de partitions de la RDD en entrée • Règle 2 : Input RDDs se base directement sur le stockage sous-jacent (bloc hdfs  partition du RDD  tâche) • Règle 3 : le degré de parallélisme doit être optimal (pas trop petit par rapport aux ressources allouées, et pas trop grand ) • Règle 4 : Il y a deux façons pour définir son degré de parallélisme (nombre des partitions) • Degré de parallélisation donné en paramètre aux transformations qui engendrent un shuffle • En utilisant la fonction repartition(numPartitions) ou coalesce(numPartitions) • La fonction coalesce(numPartitions) ne fonctionnent que lorsqu’on réduit le nombre de partitions. Elle est beaucoup plus optimal que la fonction repartition, car elle n’engendre pas de shuffle.
  • 57. Sérialisation • Quand Spark transfère les données dans le réseau ou les persistent en disque, il a besoin de sérialiser les objets (classes) en des formats binaires. • Spark utilise par défaut le Java’s built in serializer. • Mais il supporte également l’utilisation d’un serializer tiers appelé « kyro »* • Sérialisation plus rapide • Représentation binaire plus compacte • Attention : les « user-defined » classes doivent implémentée l’interface Java « Serializable ». Si non, une exception de type « noptSerializableException » sera levée.
  • 58. Gestion de la mémoire • Au sein de chaque exécuteur, la mémoire est partagé entre trois fonctionnalités : Stockage RDD Shuffle et buffers d’agrégation Le code utilisateur When you call persist() or cache() on an RDD, its partitions will be stored in memory buffers. When performing shuffle operations, Spark will create intermediate buffers for storing shuffle output data. These buffers are used to store intermediate results of aggregations in addition to buffering data that is going to be directly output as part of the shuffle Memory which is necessary for the spark code (allocation of arrays for example) spark.storage.memoryFraction spark.shuffle.memoryFraction 60% 20% 20% • Règle : Ces pourcentages doivent être paramétrées en fonction du programme à lancer. • Règle : MEMORY_AND_DISK storage level est préférable que le MEMORY_ONLY storage level pour la persistance.
  • 59. Annexe : tests de montée en échelle
  • 60. Tests de performances - Programme de test • A partir d’une table de CDRs sous forme d’un fichier CSV stocké dans hdfs, calculer des agrégats (par msisdn) – Voice total count – Voice in total count – SMS in total count – Calls dispersion – Cells disperion – Voice intenational out
  • 61. Tests de performances - Algorithme spark (scala) • Lecture du fichiers HDFS  val data = Sc.textFile(nom_fichier) • Mapping (séparation des champs)  val table = data.map(ligne=>ligne.split(‘t’)) • Filtrage (CDR voix in/out et sms in uniquement)  val types_0_1_6 = table.filter(l=>l(1)==0 || l(1)==1 || l(1)==6) • Mapping (champs qui nous intéressent uniquement)  Val ready_table = types_0_1_6 .map( l => (l(4), (l(1),l(5),l(22),l(4))) • GroupByKey (regroupement par msisdn)  Val grouped_table= ready_table.groupeByKey() • Mapping (calcul des compteurs pour chaque msisdn)  Val compteurs= grouped_table.map( l=> (l._1, fonct_calcul_compteur(l._2))) • collect(récupération des résultats dans le programme du driver pour restitution stdout)  For (i<- compteurs.collect()) println(i) filter groupByKey maptextFile map map take
  • 62. Tests de performances - Plan d’exécution spark filter groupByKey maptextFile map map take filter groupByKey maptextFile map map take Stage 1 Stage 2
  • 63. filter groupByKey maptextFile map map take Stage 1 Stage 2 Fichier HDFS stocké de façon parallélisée par blocs de 256 Mo (ex : 15,4 Go = 60 * 256 Mo + 40 Mo ) RDD : abstraction primaire des données sous spark correspondant à une collection distribuée d’enregistrements dont les partitions correspondent aux différents blocs hdfs sous-jacents. RDD RDD RDDRDD RDD Array Tests de performances - Plan d’exécution spark
  • 64. • Supposons que le fichier en entrée est un fichier hdfs text (TextInputFormat) d’une taille de 15,4 Go = 60 * 256 Mo + 40 Mo • La lecture du fichier par l’application spark se fait avec la fonction « textFile() », et permet de constituer un RDD (resilient distributed data). • Le RDD est composé de 61 partitions : – Chacune des partitions est physiquement stockée dans un des 61 nœuds contenant les blocs HDFS du fichier – Ces nœuds HDFS ne sont pas nécessairement les nœuds exécuteurs alloués pour exécuter les traitement sur les données. – Les nœuds exécuteurs chargeront donc ces données en mémoire (ou en disque dur à défaut de ressources) lorsque le job sera lancé • Plan d’exécution : L’application comporte plusieurs transformations sur le RDD et une seule action. – Un Seul job est donc lancé – Ce job comporte plusieurs transformation de type Narrow, et une seule transformation de type « wide »(groupByKey). – Il comprend donc deux stages séparés par le shuffle – Le premier stage se déroule sous forme de plusieurs tâches (61 tâches exactement) – Dans une tâche de ce premier stage, la partition concernée du RDD initial subit toute les transformations du premier stage. – Le second stage comprend autant de tâche que le nombre de partitions constitués suite au shuffle et qui est donné comme paramètre à la fonction « groupByKey » Tests de performances - Plan d’exécution spark
  • 65. Jeu de test • Application du programme de calcul des compteurs en faisant varier : – La taille du Fichier CSV de CDRs – Les ressources allouées • Pour chaque combinaison (taille de fichier, ressources allouées), le programme est exécuté plus de 100 fois, avant de prendre la valeur minimale des temps d’exécution • Pour chaque taille du fichier en entrée donnée, la configuration des ressources qui donne le temps d’exécution le plus minimal est gardée
  • 66. Résultats(1) taille en Go nombre d'executeurs cœurs par excécuteurs mémoire par excuteur (Go) facteur de repartition temps d'excution minimal 0,0045 1 1 2 1 31 0,1 1 2 4 15 38 0,5 1 2 4 2 37 1 5 5 10 25 45 5 5 5 10 30 47 15 30 5 10 150 52 30 25 5 10 120 56 45 38 5 10 400 59 92 110 5 10 359 68 150 90 10 20 1200 73 184 160 5 10 718 85 200 160 5 10 782 84 225 160 5 10 790 87 240 160 5 10 1500 86 270 160 5 10 1500 90 300 160 5 10 1700 91 394 160 5 10 2000 101 2000 180 5 10 5000 247 5000 180 5 10 18000 532
  • 67. Résultats(2) 20 25 30 35 40 45 50 55 0 2 4 6 8 10 12 14 16 petits volumes •Même pour le traitement d’un fichier d’une taille de l’ordre de quelques Ko, un temps d’initialisation important est requis (de l’ordre de 30s) pour l’allocation des ressources et l’assignation des tâches. •Quand la taille du fichier est inférieure à la taille d’un bloc d’un fichier hdsf (256 Mo), une seule tâche est exécutée dans le premier stage •Cela ne sert à rien d’allouer plus de deux cœurs (nbr exec * nb cœurs/exec). •Dans le cas précédent, si on repartitionne suite au shuffle en deux partitions, il faudrait allouer exactement deux cœurs du cluster pour le job, car deux tâches seront exécutées dans le stage 2 • Dès que la taille du fichier en entrée dépasse 256 Mo, on peut commencer à bénéficier de la parallélisation dans le premier stage. •Etant donné que le capacité maximale de la queue utilisée est de 790 cœurs, nous pouvons bénéficier de la parallélisation totale lors du premier stage même avec un fichier d’une taille de 790*256 Mo = 200 Go. •Ceci explique l’évolution logarithmique constatée à partir de 256 Mo.
  • 68. Résultats(3) 35 45 55 65 75 85 95 105 0 100 200 300 400 500 600 volumes moyens •Nous constatons que le temps d’éxécution continue d’évoluer logarithmiquement sur chaque segment de 200 Go. •Au bout de chaque 200 Go, un saut au niveau du temps d’éxécution est constaté •Cela est du au fait que sur un segment de 200 Go = 790 * 256 Mo , des cœurs sont toujours disponibles pour traiter d’autres tâches en parallèle. Dès que les 200 Go sont atteints, la parralélisation est maximale. •En augmentant ensuite la taille du fichier, il faudrait attendre que les 790 cœurs aient finis leur première tâche, pour en recevoir une nouvelle. En continuant d’augmenter la taille du fichier de 200 Go, on ne fait que maximiser la parallélisation. •Dès qu’on dépasse 200 Go à nouveau, un temps d’éxécution supplémentaire est ajouté qui est celui du traitement des blocs supplémentaires par rapport aux n*790 blocs initiaux. •Et ainsi de suite … •Nous remarquons aussi que l’évolution globale peut être approximé par une évolution linéaire
  • 69. Résultats(4) 90 140 190 240 290 340 390 440 490 540 0 1000 2000 3000 4000 5000 6000 gros volumes •Dans le ce dernier graphique, nous ne prenons plus des intervalles de 200 Go. •On constate bien que l’évolution globale du temps d’exécution constatés pour les trois fichiers testés en entrée, qui sont de 500 Go, 2To, 5To, est bien linéaire. •Si nous testons plusieurs fichiers au sein de chaque segment de 200 Go, nous aurions bien constaté une évolution locale logarithmique sur chaque segment, en plus de l’évolution linéaire globale constatée