Big Data & Spark - Scala
Big Data & Spark - Scala
• Les données sont une collection de faits, tels que des nombres, des mots, des mesures, des
observations ou simplement des descriptions de choses.
• Chiffres, caractères, symboles images …
• Les données doivent être interprétables par un humain ou une machine pour en tirer un
sens il faut que cette dernière soit placée dans un contexte
C’EST QUOI LA METADATA ?
• Les données de la donnée • Type de données: Texte, image, audio, vidéo, etc.
• Décrivent les caractéristiques des • Format de données: CSV, JSON, XML, etc.
données elles-mêmes • Taille de données: en octets, kilooctets, mégaoctets,
etc.
• Aide à comprendre les données
• Date de création et de modification des données
• Source des données: site web, système d'information,
capteurs, etc.
• Auteur des données
DATA LIFECYCLE ?
DATA PIPELINE ?
Others …
Data Analyst
Analyser les données pour les transformer en informations exploitables
• Définir la stratégie Data-Driven de l’entreprise
• Créer et maintenir les bases de données de l’entreprise
• Elaborer les critères de segmentation
Skills:
• Computer Science
• Statistiques
• Utilisation Excel
• Visualisation de données
• Programmation: Python, Scala, SAS, SQL, …
BIG DATA JOBS
Data Scientist
La science des données est une méthode systématique dédiée à la connaissance, découverte via l'analyse des données
• En entreprise, optimiser les processus organisationnels pour Efficacité
• En sciences, analyser des données expérimentales/observationnelles pour dériver des résultats
Skills:
• Computer Science
• Statistiques + Mathématiques
• Programmation: Python, R, SAS, JAVA, SQL
• Machine Learning
• Visualisation
• Connaissances du domaine
BIG DATA JOBS
Skills:
• Computer Science
• Bases de données
• Traitement de données en temps réel / données massives
• Programmation: Python, Scala, SQL, …
• Connaissance ETL / ELT
• Connaissance sur le cloud
• Comprendre les facteurs de performance et les limites des systèmes
BIG DATA JOBS
BIG DATA USE CASE
L’ECOSYSTÈME HADOOP
➢ Les plateformes « On Premise »
Les services qu’embarquent les Frameworks sont installés sur les nœuds du Cluster
Exemple de Framework
Hortonworks
Cloudera
Exemple d’architecture
▪ Type StandAlone
Les métadonnées contiennent les informations des blocs de chaque fichier, la composition des
blocs et l’emplacement de ceux-ci sur les datanodes / workernodes.
Les métadonnées sont stockées sur les namenodes / masternode.
BLOCS
Les blocs ne sont rien d’autre que le plus petit emplacement continu sur votre disque dur où les
données sont stockées. En général, dans n'importe quel système de fichiers, vous stockez les
données sous la forme d'un ensemble de blocs. De même, HDFS stocke chaque fichier sous
forme de blocs dispersés dans le cluster Apache Hadoop. La taille par défaut de chaque bloc est
de 128 Mo dans Apache Hadoop 2 . x qu’on peut configurer selon les besoins.
HDFS fournit un moyen fiable de stocker des données volumineuses dans un environnement
distribué sous forme de blocs de données. Les blocs sont également répliqués pour fournir une
tolérance aux pannes. Le facteur de réplication par défaut est 3, qui est à nouveau configurable.
PROBLÈME DE PETITS FICHIERS
Le système de fichiers distribué Apache Hadoop (HDFS) a été développé pour stocker et traiter
de grands ensembles de données.
Les petits fichiers peuvent entraîner un certain nombre de complications, ce qui entraîne une
utilisation inefficace de la mémoire Namenode une dégradation du débit d'analyse des blocs et
une réduction des performances de la couche application.
Plus de fichiers signifie plus de demandes de lecture qui doivent être servies par le NameNode,
ce qui peut finir par obstruer la capacité de NameNode à le faire. Cela entraînera une
dégradation des performances et de la réactivité
INITIATION AU LANGAGE SCALA
LES RÈGLES DE BASE
Scala est un langage de programmation développé en 2001 par Martin Odersky à l'École
Polytechnique Fédérale de Lausanne (EPFL) en Suisse. La première version publique est
sortie en fin 2003.
Il est Scalable (d’où son nom Scala)
Scala est tiré du Java avec une Syntaxe beaucoup plus simple
Scala combine :
La programmation fonctionnelle :
(basée sur des fonctions. Ex : val a = List (1,2). En
réalité List() est une fonction qui derrière, combine des
Iterable, Seq…etc Mais l’utilisateur ne voit que la
fonction simple)
Et la programmation Orienté Objet :
Scala est Dynamiquement typé (Ex: val a = 2 , renvoie
un Int)
Le caractère ‘;’ en fin d’instruction est optionnel sauf si plusieurs instructions sur une ligne
• Sensible à la casse
Exemple : "NomVariable" est différent de "nomvariable".
• Le nom des classes débute par une majuscule
• Les méthodes commencent par une minuscule
Exemple : def meMethode()
• Le nom du fichier du programme doit correspondre au nom de l’objet en question
La fonction def main(args: Array[String]) est obligatoire et représente le point d’entrée du
programme
• Les noms de variables, d’objets, de classes, de fonctions débutent par une lettre ou un underscore.
Exemple : "nom" , "_nom" , "_1_nom"
• Commentaires:
// …mon commentaire => une ligne
/* …mon commentaire */ => multiple lignes
• Import de modules:
import [Link]._ => importe toutes les méthodes dans me
import [Link] => n’importe que la méthode porterHabit
import [Link].{seLaver, porterHabit} => n’importe que les méthodes porterHabit et seLaver
LES VARIABLES
Syntaxe Générale
Keyword nomVaribale [ : DataType] = valeur
Les valeurs de keywords peuvent être:
val : Pour désigner une variable immuable (dont le contenu est non modifiable après assignation)
var : Pour désigner une variable mutable (dont le contenu est modifiable après assignation)
lazy val : Pour désigner une variable qui n’est évaluée que lorsqu’elle est appelée
Exemple : var maProfession = ’’data engineer’’
On peut déclarer plusieurs Variables en même temps:
Exemple 1 : val (a,b,c) = (1,2,3) # a, b, c valent 1,2,3
Exemple 2 : val a,b = 100 # a et b valent 100
EXERCICE : Variables
▪ Créer une variable mutable nommée « profession » et renseigner la valeur : Data Engineer
Vous avez été muté et vous êtes maintenant « scrum master ». Changer votre profession.
▪ Créer en une seule commande les variable nom, prenom et age. Pour des raisons de fraudes
sur l’identité de la personne, personne ne doit pouvoir modifier les valeurs de ces variables.
▪ Afficher les variables à l’aide de la fonction println(" ")
println(s" Mon age est $age")
println(" Et mon nom complet est %s %s".format(nom, prenom))
EXERCICES : Manipulation des String
Utiliser ces fonctions et commenter le résultat
val proverb = ‘’Je suis informaticien spécialisé dans le domaine de la data’’
▪ proverb(0) ▪ [Link]('e’)
▪ [Link] ▪ [Link](lettre => lettre =='e’)
▪ [Link] ▪ [Link]("e", "a")
▪ proverb. toUpperCase ▪ [Link](" Une autre phrase")
▪ [Link](0,9) ▪ [Link](lettre => lettre !='e')
EXERCICE : Variables
1. Créer une variable « maPhrase » avec la valeur "apprendre à positiver ses emotions pour
être en harmonie avec soi-meme et avec les autres"
2. A l’aide des fonctions prédéfinies sur les String, récupérer la sous chaine "en harmonie
avec soi-meme" et stocker là dans une variable mutable nommée « motivation »
3. Remplacer "soi-meme" se trouvant dans la variable « motivation » avec "autrui"
4. Compter le nombre d’occurrences de la lettre ‘a’ dans la variable « motivation »
LES OPERATEURS
▪ Opérateurs de comparaison
== ➔ égalité
!= ➔ différent
>, >= ➔ strictement supérieur et supérieur ou égal
<= ➔ strictement inférieur et inférieur ou égal
▪ Opérateurs logiques
&& ➔ ET logique
|| ➔ OU logique •
! ➔ NOT
▪ Opérateurs d‟affectation
=, +=, -=, *=, /=, %=
EXERCICE : Opérateurs
▪ Combien de temps (en Jours et en Heure) il faut à un marcheur pour parcourir une distance de 750km à une vitesse de 4.8km/h
NB : Vitesse = Distance/Temps et la fonction println() permet d’afficher un résultat
▪ Le gouvernement a décidé d'offrir une prime de 300€ à certains fonctionnaires en fonction de leur salaire et de leur ancienneté.
Comme toutes les autres mesures prises par le gouvernement, il est difficile de comprendre à qui cette mesure s'applique.
De ce que vous avez compris, une personne peut toucher à la prime si :
• Critère 1 : Elle a moins de 5 ans d'ancienneté et son salaire est strictement inférieur à 1500 euros.
• Critère 2 : Elle a entre 5 et 10 ans d'ancienneté et son salaire est compris entre 1500 et 2300 euros.
• Critère 3 : Elle a plus de 10 ans d'ancienneté et son salaire est strictement inférieur à 1500 euros ou supérieur à 2300 euros. C'est
à dire qu'une personne ayant plus de 10 ans d'ancienneté et un salaire entre 1500 et 2300 euros ne peut pas toucher à cette prime.
▪ Peuvent – ils toucher aux primes ?
Bernadette a 12 ans d'ancienneté et un salaire de 2400 euros.
Marc a 6 ans d'ancienneté et un salaire de 1490 euros.
LES COLLECTIONS
❑ Les Tableaux (ou Array)
Syntaxe :
▪ En initialisant la taille du tableau
val tab = new Array[DataType](longueur)
▪ En précisant les valeurs par défaut
val tab2 [:Array[T]] = Array(value1, value2…., valueN)
Exemple :
val tab = new Array[Int](3) / val tab2 : Array[Int] = Array(2,2,3)
NB : Les array sont mutables
❑ Les Tableaux (ou Array)
Ajout de valeurs (avant - après) :
val v1 = Array(4,5,6)
val v2 = v1 :+ 7 // Array(4, 5, 6, 7)
val v3 = v2 ++ Array(8,9) // Array(4, 5, 6, 7, 8, 9)
val v4 = 3 +: v3 // Array(3, 4, 5, 6, 7, 8, 9)
val v5 = Array(1,2) ++: v4 // Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
❑ Quelques fonctions natives :
val nums = Array(1,2,3) // oubien (1 to 3).toArray
[Link](_ * 2) // Array(2, 4, 6)
[Link](element => element < 3) // Array(2)
[Link](2) // 1
[Link] // 3
EXERCICE : Tableaux
1. Créer un tableau nommé Fruits
▪ Initialiser le tableau avec les Valeurs « Pomme », « Banane », « Poissons », « Mangue »
2. On s’est rendu compte tardivement que les « Poissons » ne sont pas des fruits: Supprimer le
du tableau.
3. Rajouter à la liste de fruits suivante à ’Fruits’ : [‘Orange’, ‘Papaye’]
4. Quelle est la nouvelle taille de ‘Fruits’ ?
5. A quel index se trouve la ’Mangue’ ?
❑ Les Listes
Syntaxe :
val maListe: [List[T]] = List(element1, element2,… elementN)
Val maListe = element1 :: element2 :: ….. :: ElementN :: N
val maListe = List()
Exemple :
val maListe : List[String] = List("a", "b", "c", "d", "a")
NB : Les sont immutables
❑ Les Listes
Ajout de valeurs (avant - après) :
val x = List(1,2,3)
val y = 0 :: x //List(0, 1, 2, 3)
val z = y ::: List(4,5) // List(0, 1, 2, 3, 4, 5)
val l = [Link](y, List(4,5)) // List(0, 1, 2, 3, 4, 5)
val l2 = y :+ 5 // List(0, 1, 2, 3, 4, 5)
Quelques fonctions natives : val maList: List[String] = List("a", "b", "c", "d", "a", "z")
• [Link](0, 2) ➔ List("a", "b") • [Link]("b") ➔ true
• [Link](1) ➔ z • [Link] ➔ 6
• [Link](x => x !="b" ) => List("a", "c", "d", "a") • [Link](x => x =="a") ➔ 2
EXERCICE : Listes
Soit la liste suivante : ["pomme", "banane", "goyave", "banane", "kaki", "pomplemousse"]
1. Créer la liste et stockée la dans une variable « Fruits »
2. Afficher le 1er et le 3e élément de la liste
3. Ajouter les fruits « ananas » et « pastèque » respectivement au début et à la fin de la liste Fruits et
stocker la nouvelle liste dans une liste « Fruits_1 » en une ligne de commande
4. Quelle est la nouvelle taille de « Fruits_1 »
5. Vérifier que « goyave » fait partit de « Fruits_1 »
6. Compter le nombre de fois que « banane » apparait dans « Fruits_1 »
7. Dans une variable « Fruits_sorted », trier les fruits de « Fruits_1 »
8. Supprimer tous les fruits de « Fruits_sorted » dont la taille dépasse 5 caractères et stocker dans une
variable « Fruits_small »
9. Afficher « Fruits_small »
❑ Les Set
Syntaxe :
val maSet: [Set[T]] = Set(element1, element2,… elementN)
Exemple : val maSet : Set[Int] = Set(1, 8, 4, 9)
NB : Les Set ne contiennent pas de doublons
❑ Les Set
Ajout et suppression :
val maSet = Set(1,2,3)
val y = maSet + 0 //Set(1, 2, 3, 0)
val z = y ++ Set(4,5) // Set(0, 1, 2, 3, 4, 5)
val l = z - 2 // Set(0, 1, 3, 4, 5)
val l2 = l -- Set(1, 2, 3, 0) // Set(4, 5)
Quelques fonctions natives :
val maSet1 : Set[Int] = Set(1, 8, 4, 9) val maSet2 : Set[Int] = Set(1, 8, 5, 0, 12)
• [Link] ("-") ➔ "1-8-4-9" • [Link](1) ➔ Set(1) • [Link](4) ➔ true
• [Link](10) ➔ false • [Link](maSet2) ➔ Set(1, 8) • [Link](1) ➔ Set(8, 4, 9)
• [Link](_ > 4) ➔ Set(8, 9) • [Link](maSet2) ➔ Set(4, 9)
EXERCICE : Set
▪ Créer un Set contenant les éléments "I","am","trained","to be", "a", "data", "engineer"
▪ Récupérer les éléments contenant la lettre "a"
▪ En utilisant map, transforme le Set en une collection contenant la taille des éléments de
l'ensemble.
▪ filtrer les valeurs paires
▪ Calculer la somme totale de cet ensemble d'entiers
❑ Les Tuples
Syntaxe :
val maTup : TupleX[T1, T2, …] = TupleX(el1, el2, …)
Exemple :
val maTup : Tuple3[String,String,Int]=Tuple3("dia", "mor", 28)
Quelques fonctions natives :
val maTup : Tuple3[String,String,Int]=Tuple3("dia", "mor", 28) •
maTup._1 ➔ "dia"
maTup._3 ➔ 28
[Link](2) ➔ "mor"
[Link] ➔ 3
EXERCICE : Tuples
▪ Créer un Tuple "moi" contenant votre prenom, nom, taille, sexe
▪ Accéder à la votre taille
▪ Renvoyer la phase suivante: Je suis prenom nom, j'ai une taille de valTaille et de sexe
valSexe.
❑ Les Map
Syntaxe :
val maMap: Map[K, V] = Map(k1 -> v1, k2 -> v2, …)
Exemple :
val maMap : Map[String, String] = Map("nom"-> "dia", "prenom"->"mor", "age"->"28")
Quelques fonctions natives :
val maMap: Map[String, String] = Map("nom"-> "dia", "prenom"->"mor", "age"->"28")
[Link] ➔ Set(nom, prenom, age)
[Link] ➔ MapLike(dia, mor, 28)
[Link]("sex", "Key unknown !") ➔ " Key unknown !"
[Link](_._1 == "age") ➔ Map(age -> 28)
[Link] ➔ 4
EXERCICE : Map
❑ IF – ELSE
Syntaxe générale : Exemple : if (1==2){
If (condition) { print(“true”)
// code }else {
} else if (condition) { print(“False”)
// code }
}else {
// code
}
La boucle For La boucle While La boucle Do - While
❑ Match - Case
Syntaxe générale : Exemple :
var age = 18
nomVar match {
age match {
case exp1 => instruction1
case 18 => println("Vous avez 18 ans.")
case exp2 => instruction2 case x if (x < 18) => println("Vous avez moins de 18 ans.")
… case 19 | 20 => println("Vous avez 19 ou 20 ans.")
case _ => instruction par défaut case _ => println("Vous avais plus de 20 ans")
}
}
EXERCICE : Les structures de contrôles
Copier la fonction suivante dans votre ide, au niveau de la fonction Main :
def randomAge(): Int={
val r = [Link]
[Link](100)
}
Déclarer une variable « mon_age » qui est égale randomAge()
Vérifier si vous êtes un enfant, un adolescent un adulte ou un papi. En sachant que :
0 < enfant < 9
10 <= adolescent<= 18
18 < adulte <= 60
Papi > 60
UTILISATION DE SPARK / SCALA
DÉFINITION - OBJECTIFS - ARCHITECTURE
Apache Spark est un système de traitement distribué (répartis sur plusieurs machines) open source utilisé pour les
traitements de données à grande échelle. Spark est en développé en scala.
▪ En 2009, Spark fut conçu par Zaharia lors de son doctorat au sein de l'université de Californie et en 2013,
transmis à la fondation Apache.
▪ En 2014, Spark a gagné le Daytona GraySort dont l'objectif était de trier 100 To de données le plus rapidement
possible.
▪ Ce record était préalablement détenu par Hadoop. Pour ce faire, Spark a utilisé 206 machines obtenant un
temps d'exécution final de 23 minutes alors que Hadoop avait utilisé 2100 machines pour un temps d'exécution
final de 72 minutes.
▪ La puissance de Spark fut démontrée en étant 3 fois plus rapide et en utilisant approximativement 10 fois
moins de machines.
❑ Spark vs Mapreduce
• Sous MapReduce, les données sont lues et écrites (sur disque) à chaque fois entre deux opérations
• Ces lectures et écritures dans HDFS successives rallongent significativement les temps de latence
❑ Spark vs Mapreduce
Avec Spark, le « stockage » en mémoire effectué entre plusieurs opérations est beaucoup plus rapide.
• Un autre point qui donne à Spark des performances supérieures à celle de MapReduce : les
évaluations paresseuses (“lazy evaluation”)
• Dans le cas où le cluster ne possède pas de mémoire suffisante, les données sont écrites/lues sur
disque de la même façon que Hadoop MapReduce.
▪ Spark a été crée pour améliorer MapReduce qui est un sous projet de Hadoop
▪ Mapreduce: les données sont écrites sur le disque après chaque opération
▪ Spark exécute la totalité des opérations en mémoire RAM . Il ne s'appuie sur des disques
seulement lorsque sa mémoire n'est plus suffisante.
▪ De ce fait, là où MapReduce travaille par étape, Spark travaille sur la totalité des données en
même temps.
▪ Spark est capable de travailler avec une partie des données en mémoire et une autre sur
disque
Le Framework Spark possède plusieurs fonctionnalités.
▪ Le Spark Core : c’est le système central de Spark. C’est une brique dédiée au traitement
distribué des données (comme Hadoop MapReduce).
▪ Les modules avancés : Ils sont développés au-dessus de Spark Core et permettent de faire des
traitements complexes (Streaming, machine learning, SQL…)
L’architecture Spark est assez analogue à l’architecture master/slave développée dans Hadoop
MapReduce v1. Sous Spark, un programme exécuté est appelé une application Spark.
L’architecture Spark est formée par 4 entités distinctes:
▪ Le Driver.
▪ Le master / cluster manager
▪ Les slaves / Workers
▪ Les exécuteurs
▪ Spark driver : est une JVM à laquelle on a alloué des ressources (cpu, mémoire, cœurs etc…) Le
Driver n'exécute pas de calculs (filtrer, mapper, reduce, etc.) Lorsque vous appelez collect() sur un
RDD ou un ensemble de données, toutes les données sont envoyées au Driver Le driver est la
machine qui exécute le code main(), il répartie les taches aux exécuteurs Le driver est la JVM qui
possède l’ objet appelé le SparkContext Spark application (souvent appelée Driver Program or
Application Master) est composé de SparkContext et d'un User Program
▪ Spark context : C’est une instance de l’application Spark ou une connexion
▪ Le Spark master aussi appelé le cluster manager : est chargé de négocier l’allocation de ressources
nécessaires aux JVMs. Il se charge également de trouver de l’espace et des ressources pour faire
tourner les workers et les exécuteurs. C est lui qui instancier les Workers
▪ Les Workers sont des JVMs auxquelles on a alloué des ressources (CPU, mémoire, cœurs etc…).
▪ Plus précisément, ce sont des conteneurs Spark dans lesquels vivent les exécuteurs. Sur un clusters
Hadoop, les workers sont généralement présents dans les datanodes.
▪ Les exécuteurs sont des agents situés dans les Workers et qui exécutent des « tasks»
▪ Ils sont dit multi-cœurs ou multithreads
▪ Quand le code est exécuté, les transformations de RDD sont traduites en DAG RDD (plan logique) et soumises à
▪ DAGScheduler qui génère un plan d'exécution physique (DAG de Stage) à partir de notre plan logique et le
soumet au
▪ TaskScheduler qui est responsable de l'envoi des tâches aux exécuteurs, de réessayer en cas d'échec
▪ Les tâches sont exécutées sur les WokersNode , leurs résultats sont retournés au Driver qui fera l’agrégation
Initialisation d’une session Spark
▪ Point d’entrée principal d’une exécution Spark (Main)
▪ A l’initialisation d’une Session (ex: spark-shell), l’objet SparkSession est disponible comme
avec le nom spark
import [Link]
val spark:SparkSession = [Link]()
.master("local")
.appName("mon application")
.getOrCreate()
[Link]
▪ SparkContext a été utilisé comme canal pour accéder à toutes les fonctionnalités de Spark.
De façon analogue au SparkContext est le point d’entrée de toutes les fonctions du module Spark SQL
Le Driver spark l’utlise pour se connecter au gestionnaire de ressources (YARN ou Mesos ..).
▪ SparkConf est requis pour créer l'objet Sparkcontexte , qui stocke les paramètres de configuration
comme appName (pour identifier votre driver ), l'application, le nombre de cœurs et la taille de la
mémoire de l'exécuteur dans les workers pour utiliser les API SQL, HIVE ……
▪ SQLContext est votre passerelle vers SparkSQL
Depuis la version 2.0 de Spark (juin 2016), on favorise une nouvelle classe nommée SparkSession
comme point d’entrée de Spark SQL. Cela étant, pour des raisons de comptabilité, la classe SQLContext
reste également valable.
RDD & DATASET / DATAFRAME
❑ Les RDDs
Resilient Distributed Dataset, sont des « collections » d’éléments partitionnées et distribuées à
travers les nœuds du cluster
C’est la structure fondamentale de base dans Spark
Les RDD permettent d’effectuer des opérations parallèles en mémoire sur un cluster.
Quelques caractéristiques :
▪ Traitement en mémoire
▪ Lazy evaluation
▪ Immuables
❑ Les RDDs : Création
Il existe deux (2) manières de créer un RDD
A partir d’une collection :
[Link](collection)
Exemple:
val fruits= Seq((1,"orange" ),(2,"pomme"),(3,"fraise"),(4,"citron"))
val fruitRDD = [Link](fruits)
[Link]().foreach(println)
A partir d’une source externe :
[Link]("cheminVersFichier")
❑ Les RDDs : Opérations
De manière générale, les rdds supportent 2 types d’opérations:
1. Les Transformations : 2. Les Actions :
A partir d’un RDD, retourne un autre RDD après un traitement A partir d’un RDD retourne une valeur
map(func) reduce(func)
filter(func) collect()
flatMap(func) count()
groupByKey(([numPartitions]) first()
reduceByKey(func, [numPartitions]) countByKey()
sortByKey([ascending], [numPartitions]) foreach(func)
join(otherDataset, [numPartitions ])
coalesce(numPartitions)
repartition(numPartitions)
Exemple : Les RDDs
le_renard_et_les_raisins.txt
Considérons le code suivant. Il permet de : Certain Renard Gascon, d'autres disent
Normand,
• Lire un fichier (le_renard_et_les_raisins.txt) Mourant presque de faim, vit au haut d'une
treille
• Convertir les lettres en majuscules Des raisins mûrs apparemment