Maintenant qu'on maîtrise HDFS et Avro, on est en mesure de créer notre master dataset qui va nous permettre de réaliser des analyses sur nos données. Dans ce chapitre, on va commencer par voir une manière simple et efficace d'agencer nos données ; puis on va découvrir un scénario un peu avancé d'exploitation de nos données.
Organisation des données
Tout d'abord, nous allons formaliser l'organisation hiérarchiques de nos fichiers dans HDFS. Je vous propose d'adopter l'organisation suivante :
/data/
dataset1/
raw/
.snapshot/
master/
objet1.avsc
objet2.avsc
full/
.snapshot/
sous-dataset-1/
sous-dataset-2/
...
dataset2/
raw/
.snapshot/
master/
objet3.avsc
objet4.avsc
full/
.snapshot/
sous-dataset-1/
sous-dataset-2/
...
...
Cette organisation ne constitue pas un standard absolu, mais elle a plusieurs avantages. Toutes les données sont contenues dans le répertoire/data
. Cela nous permet d'utiliser HDFS pour stocker autre chose que des master datasets. Par exemple, vous pouvez utiliser HDFS pour partager des fichiers entre les membres de votre équipe. N'oubliez pas de définir les permissions du répertoire/data
pour empêcher l'accès en écriture à la plupart des utilisateurs.
Pour chaque jeu de données ("dataset"), un sous-répertoire de/data
est créé, ce qui permet de travailler sur plusieurs jeux de données en même temps. Chaque jeu de données contient un sous-répertoireraw
etmaster
.raw
est destiné à recevoir les données brutes, tandis quemaster
contiendra les fichiers sérialisés*.avro
dans des sous-répertoires : le sous-répertoirefull
contient le master dataset complet. Vous voudrez probablement créer des sous-jeux de données de votre master dataset complet afin de réaliser des expériences, des prototypes ou des modèles partiels sans avoir à charger une grosse quantité de données.
Le ou les schémas de vos données se trouveront dans les répertoiresmaster
correspondant, ce qui vous permet de les charger à la volée dans vos différentes applications.
Il est fortement recommandé de profiter de la fonctionnalité de snapshots offerte par HDFS et qui vous prémunit d'une altération accidentelle de vos données. Les répertoire pour lesquels il faut créer des snapshots en priorité sont les répertoiresraw/
etmaster/full
. Attention toutefois : les snapshots ne vous protègent pas d'une suppression complète de votre répertoire. Par exemple, si le répertoiredataset1/master/full
est supprimé, alors les snapshots de ce répertoire seront supprimés également. À vous de définir les permissions adéquates sur vos répertoires pour empêcher que cela se produise.
Exploitation de notre jeu de données avec Spark SQL
Il nous reste à donner un exemple d'exploitation de nos données. Pour cela, on va utiliser Apache Spark, qui est une solution de calcul distribué. C'est un outil assez avancé, qu'on ne va pas s'attarder à décrire en détails puisqu'un cours entier lui est déjà consacré sur Openclassrooms (on me souffle qu'il est excellent).
En deux mots, Spark permet de réaliser des calculs distribués sur des données massives en décomposant les calculs en des successions d'opérations élémentaires dont chacune ne traite qu'une petite partie des données. Pour faire encore plus simple : avec Spark vous pouvez faire tourner des calculs très complexes sans tomber à court de RAM. (là c'est vraiment très simplifié)
Comment faire ? Pour commencer on va installer Spark :
$ cd ~/code/
$ wget http://d3kbcqa49mib13.cloudfront.net/spark-2.1.0-bin-hadoop2.7.tgz
$ tar xzf spark-2.1.0-bin-hadoop2.7.tgz
$ cd spark-2.1.0-bin-hadoop2.7/
Puis créez dans~/code
le scriptanalyse-spark.py
suivant :
import os
from pyspark import SparkContext
from pyspark.sql import SparkSession, Row
from io import BytesIO
import json
import fastavro
sc = SparkContext()
spark = SparkSession.builder.getOrCreate()
# Load files
rdd = sc.binaryFiles('hdfs://localhost:9000/data/paris/master/full/*.avro') # (filename, content)
# If it takes too long to process all files, you may want to reduce the number
# of processed files. E.g:
# rdd = sc.binaryFiles('hdfs://localhost:9000/data/paris/master/full/2.250182*.avro') # (filename, content)
# Parse avro files
nodes = rdd.flatMap(lambda args: fastavro.reader(BytesIO(args[1])))
# Convert to a resilient distributed dataset (RDD) of rows
rows = nodes.map(lambda node: Row(**node))
# Convert to a Spark dataframe
df = spark.createDataFrame(rows)
# Cache data to avoid re-computing everything
df.persist()
print("There are %d nodes in the dataset" % df.count())
print("There are %d restaurants in Paris" % df.filter(df.tags.getItem("amenity") == "restaurant").count())
print("Here are the most active users:")
df.groupBy("username").count().orderBy("count", ascending=False).limit(10).show()
Vous pouvez alors exécuter ce script avec Spark en exécutant la commande :
$ ./bin/spark-submit ../analyse-spark.py
Et le résultat :
There are 4567316 nodes in the dataset
There are 6001 restaurants in Paris
Here are the most active users:
+-----------+------+
| username| count|
+-----------+------+
| Pieren|802121|
|Esperanza36|378285|
| maouth-|346310|
| osmmaker|306224|
| mat|235226|
| cquest|226422|
| Goon|220388|
| Mawie|170594|
| vincent_95|138373|
| Truchin|133296|
+-----------+------+
Attention, l'exécution du script risque de prendre du temps ! Après tout, il s'agit d'analyser les données géographiques de tout Paris. Pour accélérer les choses, n'hésitez pas à constituer un sous-dataset et à exécuteranalyse-spark.py
sur ce sous-dataset, comme mentionné dans le code source du script. Évidemment, on obtiendrait les résultats beaucoup plus rapidement avec un cluster Spark composé de plusieurs machines ; cette expérience vous permet simplement d'avoir un aperçu des possibilités offertes par la combinaison d'un stockage distribué et d'un format de données semi-structuré.
Conclusion
Vous arrivez au terme de ce cours... plus qu'une petite activité et c'est terminé ! Vous y avez appris à sérialiser vos données avec Avro pour vous constituer un data lake stocké dans HDFS. À l'avenir, si vous êtes amenés à participer à des projets qui utilisent des données massives (ce qu'on vous souhaite), vous allez probablement rencontrer des architectures légèrement différentes de celle proposée dans ce cours. HDFS est une brique incontournable, mais le format de sérialisation des données, ainsi que leur organisation, varient d'un projet à l'autre. Il existe par exemple de nombreuses alternatives à Avro, comme Protocol Buffers ou Apache Thrift. Ne soyez pas surpris ! Ces outils sont différents, mais au fond ils reposent tous largement sur les mêmes principes. Sachez généraliser vos connaissances à des domaines proches de ce que vous connaissez.
Joyeux stockage !