Retour aux Articles
Dec 05, 2024
5 min de lecture

Delta Tables : Maîtriser la Data Compaction pour des performances optimales

Dans le monde du big data, une gestion efficace des données est cruciale. Grâce à ses propriétés ACID et à ses optimisations de performance, Delta Lake constitue une base robuste. Une de ses fonctionnalités clés pour maintenir des performances optimales est la data compaction, aussi appelée opération OPTIMIZE. Cet article explore en profondeur la data compaction dans les Delta Tables, et fournit un guide complet pour les data engineers et les architectes.

Qu’est-ce que la Data Compaction et pourquoi est-ce important ?

La data compaction consiste à fusionner les petits fichiers d’une Delta Table en fichiers plus volumineux. Au fil du temps, en raison des insertions, mises à jour ou suppressions, une table peut accumuler un grand nombre de petits fichiers, ce qui entraîne :

  • Dégradation des performances des requêtes : lire de nombreux petits fichiers génère une surcharge importante (I/O, métadonnées, scheduling), ce qui ralentit l’exécution des requêtes.
  • Augmentation des coûts de stockage : les systèmes de fichiers ont souvent une taille minimale allouée par fichier, entraînant du gaspillage lorsque de nombreux petits fichiers sont présents. Les métadonnées prennent aussi de la place.
  • Gestion inefficace des données : trop de fichiers compliquent les sauvegardes, la restauration et la gestion du cycle de vie des données.

La data compaction résout ces problèmes en consolidant les fichiers, ce qui permet :

  • Amélioration des performances : moins de fichiers = moins de surcharge I/O = exécutions plus rapides.
  • Réduction des coûts de stockage : consolidation = espace optimisé.
  • Gestion simplifiée : moins de fichiers = gestion plus facile.

Comment fonctionne la Data Compaction dans Delta Lake

La data compaction repose sur plusieurs mécanismes internes :

Fusion et réécriture de fichiers

Quand vous exécutez OPTIMIZE, Delta Lake identifie les petits fichiers (en fonction d’un seuil configuré) et les fusionne. Le processus comprend :

  1. Identification des petits fichiers
  2. Lecture des données
  3. Fusion et réécriture en fichiers Parquet plus gros
  4. Enregistrement dans le transaction log comme une transaction atomique
  5. Mise à jour des métadonnées : les nouveaux fichiers remplacent les anciens

Stratégies et options de compaction

Delta Lake permet de configurer la compaction :

  • Taille cible des fichiers : souvent 1 Go, mais à ajuster selon le workload
  • Partitionnement : possibilité de compacter uniquement certaines partitions
  • Z-Ordering : clusterisation spatiale des données basée sur des colonnes spécifiques pour améliorer les performances de filtrage/join
  • spark.databricks.delta.optimize.maxFileSize : taille cible pour OPTIMIZE (au niveau cluster)
  • spark.databricks.delta.optimize.minPartitions : nombre minimal de partitions pour parallélisme

Intégration avec le Transaction Log

Chaque OPTIMIZE est enregistré comme une transaction Delta :

  • Atomicité : l’opération est “tout ou rien”
  • Cohérence : la table reste lisible et valide à tout moment
  • Durabilité : les changements sont persistés même en cas de crash

Cela permet aussi le time travel vers l’état avant ou après la compaction.

Compaction en pratique : Exemples de code

PySpark

from pyspark.sql import SparkSession
from delta.tables import DeltaTable

spark = SparkSession.builder.appName("DeltaCompaction").getOrCreate()
table_path = "/path/to/my_delta_table"

# OPTIMIZE de base
spark.sql(f"OPTIMIZE delta.`{table_path}`")

# OPTIMIZE avec filtre de date
spark.sql(f"OPTIMIZE delta.`{table_path}` WHERE date >= '2023-01-01' AND date < '2023-02-01'")

# OPTIMIZE avec Z-Ordering
spark.sql(f"OPTIMIZE delta.`{table_path}` ZORDER BY (id)")

# API DeltaTable
deltaTable = DeltaTable.forPath(spark, table_path)
deltaTable.optimize().executeCompaction()
deltaTable.optimize().where("date >= '2023-01-01' AND date < '2023-02-01'").executeCompaction()
deltaTable.optimize().zOrderBy("id").executeCompaction()

spark.stop()

Scala

import org.apache.spark.sql.SparkSession
import io.delta.tables._

object DeltaCompaction {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().appName("DeltaCompaction").getOrCreate()
    val tablePath = "/path/to/my_delta_table"

    spark.sql(s"OPTIMIZE delta.`$tablePath`")
    spark.sql(s"OPTIMIZE delta.`$tablePath` WHERE date >= '2023-01-01' AND date < '2023-02-01'")
    spark.sql(s"OPTIMIZE delta.`$tablePath` ZORDER BY (id)")

    val deltaTable = DeltaTable.forPath(spark, tablePath)
    deltaTable.optimize().executeCompaction()
    deltaTable.optimize().where("date >= '2023-01-01' AND date < '2023-02-01'").executeCompaction()
    deltaTable.optimize().zOrderBy("id").executeCompaction()

    spark.stop()
  }
}

Configuration et planification de la compaction

Exécution manuelle

Utilisez les scripts ci-dessus pour des optimisations ponctuelles ou déclenchées par des événements.

Compaction planifiée

  • Databricks Jobs : exécutez périodiquement des notebooks contenant des requêtes OPTIMIZE
  • Apache Airflow : DAG planifié avec tâche SparkSubmit
  • Cron job : script shell/Python déclenché via cron (moins robuste)

Exemple DAG Airflow (Python) :

from airflow import DAG
from airflow.operators.spark_submit_operator import SparkSubmitOperator
from datetime import datetime

with DAG(
    dag_id="delta_compaction",
    schedule_interval="0 3 * * *",
    start_date=datetime(2023, 1, 1),
    catchup=False,
    tags=["delta", "compaction"],
) as dag:
    compact_task = SparkSubmitOperator(
        task_id="compact_delta_table",
        application="/path/to/compaction_script.py",
        conn_id="spark_default",
        application_args=["/path/to/my_delta_table"],
    )

Bonnes pratiques pour la compaction

  • Définir la fréquence : dépend du volume d’ingestion, des patterns de requêtes. Exemples : quotidien, hebdomadaire, horaire (cas extrême).
  • Z-Ordering ciblé : uniquement si les colonnes ciblées sont utilisées dans les filtres/joins.
  • Monitorer l’impact : avant/après (latence, taille fichiers, CPU/mémoire).
  • Respecter la rétention : compacter peut fusionner plusieurs versions ; attention au time travel.
  • Équilibrer taille/fréquence :
    • gros fichiers = meilleures perfs mais compaction coûteuse
    • petits fichiers = compaction fréquente mais amélioration limitée
  • Auto Compaction (Databricks) : à activer si dispo, mais à monitorer
  • Tuning Spark : ajuster spark.sql.shuffle.partitions, mémoire, cores
  • Tests pré-production : toujours valider dans un environnement de test

Débogage de la Data Compaction

  • Performances dégradées après compaction : mauvais choix de colonnes pour Z-Ordering, fichiers trop gros
  • Problèmes de ressources : compaction = gourmande ; augmenter mémoire, parallélisme ou baisser maxFileSize
  • Durée excessive :
    • table énorme
    • partitionnement inefficace
    • ressources limitées
    • taille cible trop faible
  • Conflits concurrents : éviter les écritures concurrentes pendant la compaction
  • Problèmes Spark : logs à surveiller (mémoire, exécuteurs, erreurs système)

Conclusion

La data compaction est essentielle pour maintenir la performance et maîtriser les coûts dans un environnement Delta Lake. En comprenant son fonctionnement et en appliquant les bonnes pratiques, vous garantissez un système stable, rapide et scalable. Planifiez, surveillez, et testez — c’est la clé pour exploiter au mieux la puissance de OPTIMIZE.