📅 ✍️ difTech

Il y a deux ans, nous avons migré plusieurs pipelines de production vers Apache Iceberg. Ce qui a commencé comme un projet exploratoire est devenu notre standard pour les architectures lakehouse. Cet article est un retour d’expérience honnête — y compris sur ce qui a moins bien marché.

Pourquoi migrer vers Iceberg ?

La question mérite d’être posée sérieusement. Parquet + Hive Metastore a fonctionné des années. Pourquoi changer ?

ACID et transactions réelles

Le premier argument décisif : les transactions ACID. En pratique, cela signifie qu’on peut faire des DELETE, UPDATE et MERGE sur des tables massives sans avoir à réécrire l’ensemble de la partition. Pour des cas de mise en conformité RGPD (droit à l’oubli), de correction de données en production, ou de mises à jour partielles fréquentes, c’est une différence fondamentale.

-- Merge pour un upsert sur une table de 500M de lignes
MERGE INTO prod.transactions t
USING staging.transactions_updates u ON t.transaction_id = u.transaction_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *;

Avec Hive, cette opération nécessitait une réécriture complète de la partition. Avec Iceberg, elle est atomique et ne touche que les fichiers concernés.

Time Travel et auditabilité

Iceberg maintient un historique des snapshots de chaque table. Pouvoir interroger une table telle qu’elle était il y a 30 jours, sans infrastructure supplémentaire, a une valeur opérationnelle réelle :

# Lire la table au snapshot du 1er juin
df = spark.read \
    .option("as-of-timestamp", "2026-06-01 00:00:00") \
    .format("iceberg") \
    .load("s3://my-bucket/prod/transactions")

Nous l’avons utilisé pour du debugging de pipeline (comprendre une anomalie dans des données passées), pour des audits réglementaires, et pour comparer des données avant/après une transformation.

Schema Evolution sans douleur

Ajouter une colonne nullable, renommer une colonne, modifier un type compatible — tout cela se fait sans réécrire les données historiques. Iceberg gère le mapping entre l’ancien et le nouveau schéma de manière transparente.

ALTER TABLE prod.events ADD COLUMN session_duration_ms BIGINT;
-- Les anciennes lignes auront NULL pour cette colonne, sans réécriture

Architecture : S3 + Glue Catalog vs BigQuery Omni

Deux configurations que nous avons opérées en production.

AWS : S3 + Glue Catalog

C’est l’architecture la plus courante et la mieux supportée. Glue joue le rôle de catalog Iceberg — il stocke les métadonnées, les chemins vers les fichiers de metadata Iceberg, et permet à Spark, Athena et d’autres engines de lire les mêmes tables de façon cohérente.

# Configuration Spark pour Iceberg sur AWS
spark = SparkSession.builder \
    .config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions") \
    .config("spark.sql.catalog.glue_catalog", "org.apache.iceberg.aws.glue.GlueCatalog") \
    .config("spark.sql.catalog.glue_catalog.warehouse", "s3://my-warehouse/") \
    .config("spark.sql.catalog.glue_catalog.io-impl", "org.apache.iceberg.aws.s3.S3FileIO") \
    .getOrCreate()

Un point important : le Glue Catalog avec Iceberg n’est pas équivalent au Glue Catalog classique (Hive-compatible). Les tables Iceberg dans Glue ont leur propre représentation. Ne mélangez pas les deux dans le même namespace sans une stratégie claire.

GCP : Iceberg avec BigQuery Omni

BigQuery supporte nativement les tables Iceberg depuis fin 2024. Vous pouvez créer des tables BigLake pointant vers des fichiers Iceberg sur GCS et les interroger en SQL standard, tout en maintenant la compatibilité avec Spark ou Flink.

-- Créer une table BigLake sur des fichiers Iceberg existants
CREATE EXTERNAL TABLE `project.dataset.my_table`
WITH CONNECTION `us.my-connection`
OPTIONS (
  format = 'ICEBERG',
  uris = ['gs://my-bucket/warehouse/my_table/']
);

L’avantage : une seule copie des données, accessible depuis BigQuery et depuis vos jobs Spark. L’inconvénient : le support BigQuery Omni + Iceberg reste moins mature que l’écosystème AWS, et certaines opérations d’écriture complexes (merge sur large scale) peuvent avoir des comportements inattendus.

Partitioning : Hidden vs Explicit

C’est l’un des points où Iceberg brille — et où on peut aussi se tirer une balle dans le pied.

Hidden Partitioning (la nouveauté Iceberg)

Iceberg permet de définir des transformations de partitionnement qui sont cachées à l’utilisateur final. Pas besoin d’écrire WHERE year = 2026 AND month = 6 — Iceberg convertit automatiquement WHERE event_date = '2026-06-15' en un filtre sur la bonne partition.

CREATE TABLE prod.events (
    event_id BIGINT,
    event_date DATE,
    user_id BIGINT,
    event_type STRING,
    payload STRING
) USING iceberg
PARTITIONED BY (days(event_date));
-- 'days()' est une transform Iceberg — la partition est hidden

Ce qu’on a appris : days() est souvent le meilleur point de départ. months() crée trop peu de partitions sur des tables à haute volumétrie journalière. hours() peut créer des milliers de petits fichiers si votre pipeline écrit fréquemment.

Partition Evolution

Iceberg permet de changer la stratégie de partitionnement sans réécriture. Les nouvelles données suivent le nouveau schéma de partition, les anciennes restent intactes. Iceberg sait interroger les deux de façon transparente. En pratique, c’est une fonctionnalité que nous n’avons utilisée qu’une fois — mais elle nous a évité une migration de données volumineuse.

Compaction et maintenance : le travail qu’on sous-estime toujours

C’est probablement la partie la plus sous-documentée d’Iceberg en production. Un pipeline qui écrit bien mais ne compacte pas finit par avoir des milliers de petits fichiers — et les performances de lecture s’effondrent.

Le problème des small files

Chaque écriture Spark crée un fichier par tâche. Un job Spark qui tourne toutes les 5 minutes avec 50 partitions crée 50 fichiers toutes les 5 minutes. Après une semaine : 100 000 fichiers. Après un mois : 400 000 fichiers. Les scans deviennent lents, S3 devient coûteux en requêtes LIST, et le catalog grossit.

Stratégies de compaction

rewrite_data_files regroupe les petits fichiers en gros fichiers optimaux (typiquement 128 Mo à 512 Mo selon votre workload) :

from pyiceberg.catalog import load_catalog

catalog = load_catalog("glue", **{"type": "glue"})
table = catalog.load_table("prod.events")

# Compaction via Spark (plus efficace pour les grosses tables)
spark.sql("""
    CALL glue_catalog.system.rewrite_data_files(
        table => 'prod.events',
        strategy => 'binpack',
        options => map(
            'min-file-size-bytes', '134217728',
            'target-file-size-bytes', '536870912',
            'max-concurrent-file-group-rewrites', '10'
        ),
        where => 'event_date >= date_sub(current_date(), 7)'
    )
""")

expire_snapshots supprime les anciens snapshots et les fichiers de metadata associés. Sans cette opération, votre répertoire de metadata grossit indéfiniment :

spark.sql("""
    CALL glue_catalog.system.expire_snapshots(
        table => 'prod.events',
        older_than => TIMESTAMP '2026-05-01 00:00:00',
        retain_last => 5
    )
""")

Notre recommandation : orchestrez ces deux opérations quotidiennement via Airflow ou Step Functions. Un job de compaction qui tourne à 3h du matin sur les 7 derniers jours de données, et un job d’expiration hebdomadaire sur les snapshots de plus de 30 jours.

Spark : le cas le plus mature

L’intégration Spark + Iceberg est la plus mature et la mieux testée. Quelques points d’attention :

Flink supporte Iceberg en tant que sink et source (en lecture incrémentale). C’est notre stack pour les pipelines streaming qui écrivent en continu sur des tables Iceberg :

// Sink Flink vers Iceberg
FlinkSink.forRowData(dataStream)
    .table(table)
    .tableLoader(tableLoader)
    .upsert(true)
    .equalityFieldColumns(Arrays.asList("user_id", "event_date"))
    .build();

Point critique avec Flink : la taille des checkpoints. Avec Iceberg, chaque checkpoint Flink crée un snapshot Iceberg. Sur un job qui checkpoint toutes les 30 secondes, vous avez 2 880 snapshots par jour. L’expiration régulière des snapshots n’est pas optionnelle ici — c’est une nécessité.

Pièges courants

Catalog confusion. Le même fichier de données Iceberg peut être enregistré dans plusieurs catalogs (Glue, Nessie, un Hive Metastore). Si deux jobs pointent vers des catalogs différents pour la même table, vous aurez des problèmes de conflits d’écriture ou de lecture incohérente. Définissez un catalog comme source de vérité et tenez-vous y.

Ne pas monitorer la taille des manifests. Les fichiers de manifeste Iceberg listent tous les fichiers de données d’une table. Une table avec des millions de petits fichiers aura des manifestes gigantesques — et chaque requête commencera par télécharger et parser ces manifestes. La compaction résout le problème des fichiers de données ; rewrite_manifests résout celui des manifestes eux-mêmes.

Sous-estimer les coûts S3. Iceberg génère beaucoup d’opérations S3 — LIST, GET sur les fichiers de metadata. Sur des tables très actives, ces coûts peuvent surprendre. Activez le S3 Intelligent-Tiering pour les données froides, et ne négligez pas les coûts de requête dans votre TCO.

Recommandations concrètes

  1. Commencez par une table non-critique pour maîtriser les opérations de maintenance avant de migrer vos tables de production les plus importantes.
  2. Mettez en place la compaction dès le jour 1, pas quand les problèmes de performance apparaissent.
  3. Utilisez un catalog centralisé (Glue sur AWS, ou Nessie si vous voulez du multi-cloud) et documentez la convention de nommage.
  4. Monitorez la taille des snapshots et des manifests via les tables de métadonnées Iceberg (iceberg.metadata_log_entries, iceberg.manifests).
  5. Testez le time travel régulièrement — c’est aussi un bon test de santé de vos snapshots.

Iceberg en production, c’est un investissement qui paie. Mais comme tout investissement, il demande une discipline opérationnelle. Les entreprises qui l’adoptent sans penser à la maintenance finissent par avoir des tables Iceberg aussi mal gérées que leurs anciennes tables Hive.

Besoin d'un expert ?

difTech vous accompagne dans vos projets data — de l'architecture à la mise en production.

📅 Réserver un créneau gratuit