Accueil » Générer un dataset partitionné via un data flow Azure Synapse Analytics

Générer un dataset partitionné via un data flow Azure Synapse Analytics

Générer un dataset partitionné via un data flow Azure Synapse Analytics

Afin d’optimiser les requêtes effectuées sur des fichiers parquets depuis un pool serverless, on travail généralement avec des datasets partitionnés. Cet article décrit comment générer un dataset partitionné via un data flow Azure Synapse Analystics.

Introduction

Lorsque l’on observe la structure hiérarchique d’un Datalake, on tombe très souvent sur une architecture partitionnée (et le plus souvent par date). Ceci est principalement fait pour optimiser les requêtes d’extraction de données qui exploitent souvent des plages de dates bien spécifiques.

Des cas d’usages très fréquents sont :

  • Sélection des données d’un mois ou d’une année spécifique
  • Sélection des données depuis un mois ou une année spécifique
  • Sélection des données entre une plage de date

Ces cas d’usage sont fréquents et lorsque l’on a beaucoup de données, il peut devenir très consommateur de ressource de lire une infime partie des données dans de très gros fichiers parquets.

C’est là qu’intervient le partitionnement. Au lieu d’avoir un « gros » fichier parquet qui (malgré toutes les optimisations du format) demande à être parsé pour retrouver seulement les données nécessaires, on va se limiter à lire uniquement une sélection précise de petits fichiers.

Pour ce faire il faut changer la façon dont on écrit nos données et passer de :

Un « gros » fichier parquet

à :

données partitionnées en de multiples fichiers parquets

Forcément vu comme ça il y a un peut de travail. Il existe de nombreuses façons de faire ce partitioning et nous allons nous intéresser a produire celui-ci via les pipelines Azure Synapse Analytics (et ça marche aussi via ADF évidemment).

Cas d’usage – Générer un dataset partitionné de nos factures

Cet article peut se suffire à lui-même en utilisant un fichier spécifique et adaptant en conséquence nos développements, mais il utilise la sortie générée dans l’article : Paramétrer les Pipeline Azure Synapse Analytics sans ouvrir Synapse !

En prenant comme source la base WWI de Microsoft on remarque que nos factures sont décomposées en deux tables « Invoices » et « InvoicesLines ». Ce design est très standard ou une table « d’entête » comporte des données globales à plusieurs « lignes » de données.

Si ce design est particulièrement adapté au modèle relationnel, dans le monde du bigdata, on lui préfère très souvent le « grosse table à plat » dans un fichier. Ceci permet à nos scripts ou utilisateurs de se sourcer sur un seul dataset comprenant l’ensemble de leurs données et n’ayant pas besoin de faire des jointures ou autres transformations récurrentes.

Dans le cas des factures, les informations de prix, quantités et articles sont sur les « lignes » cependant la date, le client, l’adresse de livraison sont dans les entêtes. Pour s’simplifier l’usage de ces données, nous allons créer un dataset « Invoice » qui fera la jointure des deux tables et pour optimiser son requêtage futur nous allons partitionner celui-ci par Année/Mois.

Et comme nous créons un nouveau « dataset intelligent » nous allons le positionné dans la zone « Curated » de notre datalake pour pouvoir éventuellement l’exposer à des utilisateurs.

Les mains dedans !

Les ressources utilisées dans cette expérience sont:

Vue d’ensemble

La solution consiste en un data flow comprenant les activités suivantes :

  • Deux sources
  • Une jointure
  • Une colonne dérivée
  • Une cible
Vue d’ensemble du data flow

Création du data flow

Pour démarrer, nous allons donc dans la partie développement créer un nouveau data flow :

Création d’un data flow

Nous arrivons maintenant dans l’éditeur de data flow qui nous propose directement d’ajouter une nouvelle source de donnée ainsi que donner un nom à notre data flow :

Editeur de data flow

Ajout de la 1ere source

En ajoutant la source, l’éditeur nous propose de renseigner deux champs importants :

  • le « nom de la boite » : oui c’est important pour s’y retrouver plutôt que de tout laisser par défaut de de se retrouver avec source1 / source2 / … dans nos flows !
  • le « Dataset » source : on va connecter notre fichier des entêtes de factures « Invoices.parquet »

Création du dataset source

Cette manipulation est récurrente, nous allons donc la décrire pour le premier dataset et vous serez ensuite seul pour les suivants 😉 (tant qu’ils n’auront rien de particulié).

Donc très simplement, nous allons cliquer sur « + » :

Création d’un nouveau dataset

La première étape est de sélectionner le « data store », le type de stockage ou se trouve dans notre fichier. Dans notre cas, ce sera un Azure Data Lakke Storage Gen2 :

Selection du type de Data store

Nous devons ensuite sélectionner le format de fichier. Nous utilisons des fichiers parquet :

Sélection du format de fichier

Nous devons maintenant donner un nom et définir le service lié utilisé pour retrouver notre fichier. Nous choisissons donc notre datalake :

Selection du service lié

Pour ensuite configurer le chemin de notre fichier. Pour se faire, on peut remplir à la main le chemin ou utiliser le navigateur :

Définition du chemin de fichier
Parcours de notre lake

Notre dataset est maintenant configuré :

Fin de configuration du dataset

Le reste de la configuration peut rester par défaut. Nous avons la possibilité de faire une preview des données en démarrant une session de debug au besoin.

Notre source de donnée est maintenant configurée

Ajout de la 2ème source

La 2ème source ce configure exactement de la même manière que précédemment en changeant simplement le fichier source par « InvoiceLines.parquet ».

Nos deux sources sont configurées

Création de la jointure

En cliquant sur le petit « + » nous allons ajouter notre composant de jointure :

Ajout de la jointure

Le paramétrage est simple. On cherche à ajouter à chaque entête ces lignes. On fait donc une jointure gauche des « Invoices » vers les « InvoiceLines ». La clef de jointure est en général simple (si le modèle source est propre) et dans notre cas « InvoiceID » = « InvoiceID » :

Paramétrage de la jointure gauche

Création des colonnes dérivées

Jusqu’à présent, nous avons surtout préparé notre jeu de donnée. Nous arrivons maintenant au moment ou l’on va préparer notre partitionnement. Pour rappel, nous voulons partitionner par Année/Mois. Nous avons bien une date de commande, mais s’il est possible de configurer le partitionnement en utilisant une expression spécifique, je recommande vivement de générer les valeurs correctement dans des colonnes pour ensuite utilise directement cette valeur dans le partitionnement.

Pour ce faire, nous allons donc générer deux colonnes année et mois à partir de notre date de commande via les derived column. On ajoute donc le composant :

Ajout des colonnes dérrivées

La configuration est très simple dans notre cas et il suffit d’écrire les expressions correspondantes en ajoutant les nouvelles colonnes :

  • year –> year(InvoiceDate)
  • month –> month(InvoiceDate)
Création des colonnes dérivées

Création des partitions

Notre dataset est prêt, il nous reste à écrire celui-ci dans sa destination en paramétrant le partitionnement. C’est sur la destination que tout va se configurer et nous allons ajouter un récepteur :

Ajout d’un récepteur

Création du dataset de destination

Techniquement parlant, la création du dataset passe par les mêmes étapes que précédemment sauf que dans notre cas nous ne souhaitons pas « forcer » le chemin du fichier ni le nom de ceux-ci, car nous espérons justement partitionner notre dataset.

Pour se faire, il suffit de configurer notre dataset avec uniquement les informations connues pour le file path. Nous souhaitons que notre dataset soit partitionné dans la zone « curated » de notre lake et dans le répertoire « invoice » de cette zone. C’est ce que nous allons renseigner sans préciser de nom de fichier :

Définition d’un dataset générique

Notre destination est maintenant configurée :

Configuration du récepteur

Définition du partitionnement

S’il est possible de configurer très finement notre destination, la chose qui nous intéresse dans cet article est le partitionnement et pour ce faire nous avons rendez-vous dans l’onglet d’optimisation de notre récepteur.

ici plusieurs options s’offrent à nous. Cependant, tout le travail réalisé précédemment a eu pour but de définir correctement nos partitions en créant des colones spécifiques. notre choix se porte donc sur un partitionnement défini par clé pour laquelle nous allons définir dans l’ordre nos colonnes :

Définition du partitionnement

C’est cette configuration précisément qui va « éclater » notre dataset en plusieurs fichiers déposé dans des répertoires différents pour chaque valeur de la colonne « year » et à l’intérieur de celle-ci dans différents répertoires pour chaque valeur de « month ». L’interface nous indique que chaque partition aura des valeurs uniques pour chaque colonne définie.

Vérification

Il ne nous reste plus qu’à exécuter notre data flow pour partitionner notre dataset. Pour ce faire nous pouvons l’intégrer à un pipeline existant ou en créer un nouveau.

Pour l’occasion, nous allons un nouveau pipeline avec l’ajoute de l’activité « Data flow » configurée pour exécuter le data flow développé précédemment.

Création d’un pipeline de test

Nous nous assurons que notre datalake est « propre » afin de bien observer notre résultat :

datalake « vide »

Lançons maintenant l’exécution (ou le debug) de notre pipeline pour réellement traiter notre fichier :

Pipeline exécuté

Si tout s’est bien passé, après le succès de notre pipeline, nous pouvons naviguer dans notre nouveau dataset partitionné :

Dataset partitionné

Conclusion

Le partitionnement d’un dataset pour optimiser sa lecture future n’est pas compliqué à réaliser techniquement, mais demande cependant une phase de réflexion pour la définition de nos partitions.

Cet article avait pour but de décrire le partitionnement en utilisant des dataflow Synapse, mais il est évidemment possible de créer ce type de partitionnement différemment notamment avec Spark directement.

Pour aller plus loin

Pour utiliser ce nouveau dataset de façon optimal avec un pool serverless vous pouvez maintenant suivre l’excellente vidéo avec Stijn Wynants (@Azure_Synapse) et Filip Popovic (@SQLStijn) sur le sujet : Synapse Espresso: Partitioning


Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *