Que fait lakehouse ?
Aide à la décision de référence pour le stockage en format de table ouvert et le code Spark. Les versions et les dates se trouvent dans references/landscape-2026-11.md ; vérifiez-les avant toute affirmation spécifique à une version. Pour les spécificités de Databricks, Snowflake ou AWS, utilisez le skill de-vendors et les skills officiels des fournisseurs auxquels il fait référence.
1. Un lakehouse est-il justifié ?
Partez de l'option la moins chère qui convient et ne montez que lorsqu'un déclencheur listé est réel et mesuré.
- Postgres (ou DuckDB sur un seul ensemble de fichiers) lorsque les données sont inférieures à environ 1 To, qu'un seul moteur les lit et que les utilisateurs analytiques concurrents sont peu nombreux. Raison : pas de catalogue, pas de compaction, pas de cluster à exécuter.
- Entrepôt cloud lorsque de nombreux utilisateurs SQL ont besoin d'une concurrence élastique et que personne n'a besoin d'un second moteur sur les mêmes octets. Raison : le stockage, la maintenance et la gouvernance gérés sont inclus dans le prix.
- Lakehouse lorsqu'au moins une condition est remplie : dizaines de To ou plus ; plusieurs moteurs (Spark, Trino, DuckDB, Python) doivent lire une seule copie ; les charges de travail ML ou basées sur des fichiers partagent des tables avec SQL ; le coût de stockage sur le stockage objet surpasse clairement le coût de l'entrepôt à votre volume ; le format ouvert est une exigence impérative (sortie, audit, neutralité vis-à-vis des fournisseurs).
- Ne choisissez pas un lakehouse pour une « échelle future » qui n'a pas de chiffre associé. Demandez les nombres de lignes, la croissance quotidienne, les lecteurs et les moteurs concurrents avant de recommander.
- Données petites et moyennes qui souhaitent néanmoins un format ouvert : DuckLake ou simple Parquet plus DuckDB valent mieux que d'exécuter Spark.
2. Disposition Parquet
- Visez 128 Mo à 1 Go par fichier ; en cas de doute, visez 256 Mo à 512 Mo. De nombreux fichiers de moins de 32 Mo signifient que les coûts de listage et d'ouverture dominent la requête.
- Groupes de lignes d'environ 128 Mo non compressés (1M+ lignes) par fichier ; des groupes plus petits affaiblissent l'élagage min/max et la compression.
- Partitionnez uniquement par des colonnes à faible cardinalité sur lesquelles les requêtes filtrent (date ou mois, région). Une bonne partition contient au moins un fichier de taille cible complète par lot d'écrivains ; si les partitions font en moyenne moins de 1 Go, utilisez un grain plus grossier ou un clustering à la place.
- Ne partitionnez jamais par un identifiant à haute cardinalité. Utilisez le clustering (clustering liquide Delta, ordre de tri / Z-order Iceberg) pour ces colonnes.
- Triez ou clustérisez au sein des fichiers sur la colonne la plus filtrée afin que les statistiques min/max élaguent les groupes de lignes.
- Les écritures en streaming ou par micro-lots fréquents créent de petits fichiers ; planifiez la compaction, ne comptez pas sur les écrivains pour dimensionner les fichiers.
3. Choix d'un format de table
Décidez en fonction du moteur et du catalogue que vous possédez déjà, pas des listes de fonctionnalités.
- Delta Lake : choisissez lorsque Databricks ou Spark est le moteur principal, ou qu'Unity Catalog est déjà la couche de gouvernance. Intégration Spark la plus forte ; les autres moteurs le lisent mais la prise en charge de l'écriture varie.
- Apache Iceberg : choisissez lorsque plusieurs moteurs doivent écrire, ou que vous souhaitez des catalogues indépendants du fournisseur (REST). La v3 ajoute les vecteurs de suppression, la lignée des lignes et un type variant ; confirmez que chaque moteur et catalogue dans le chemin prend en charge les fonctionnalités v3 que vous utilisez avant de mettre à niveau une table, car un ancien lecteur échoue sur une version de format plus récente.
- DuckLake : choisissez pour les données petites et moyennes, une seule équipe, et une base de données SQL que vous exploitez déjà comme catalogue (les métadonnées vivent dans des tables simples). Peu coûteux à exécuter, écosystème jeune ; ne le choisissez pas lorsque Spark, Trino ou Snowflake doivent écrire les mêmes tables.
- Les couches d'interopérabilité (Delta UniForm, métadonnées Iceberg pour les tables Delta) sont des chemins de lecture. Choisissez un format comme écrivain de référence par table.
- Changer de format plus tard implique une réécriture ou une conversion de métadonnées ; choisissez délibérément l'écrivain de référence.
4. Catalogues
- Le catalogue est le coordinateur des commits et le point de contrôle d'accès ; choisissez-le avant que les détails du format n'aient de l'importance.
- Unity Catalog : le défaut centré sur Databricks ; gouvernance pour Delta et Iceberg.
- AWS Glue et S3 Tables : choisissez lorsque la pile est native AWS ; S3 Tables gère la compaction pour vous mais vous lie à AWS.
- Catalogue REST Iceberg (par exemple Polaris) : choisissez pour la neutralité multi-moteur et multi-cloud.
- DuckLake : le catalogue est une base de données SQL ; utilisez Postgres pour une utilisation multi-utilisateur, SQLite ou DuckDB uniquement pour un seul écrivain.
- Ne laissez jamais deux catalogues enregistrer le même chemin de table ; les commits sur l'un corrompront la vue de l'autre.
5. Maintenance (planifiez-la, sinon la table se dégrade)
- Compacter les petits fichiers : Delta
OPTIMIZE, Icebergrewrite_data_files, DuckLake fusion de fichiers adjacents. Exécutez après une ingestion importante, pas par écriture. - Supprimer les anciennes données : Delta
VACUUM, Icebergexpire_snapshotsplus suppression des fichiers orphelins, DuckLake expiration des instantanés puis nettoyage. La rétention doit dépasser votre lecteur ou fenêtre de voyage dans le temps le plus long, car la suppression de fichiers sous un lecteur en cours d'exécution le casse. - Clustering ou Z-order uniquement pour les colonnes qui apparaissent dans les filtres ; le re-clustering réécrit les données et coûte du calcul.
- Vérifiez le nombre de fichiers et la taille moyenne des fichiers avant et après ; un job de maintenance qui ne modifie ni l'un ni l'autre n'a rien fait.
- Essayez d'abord un vacuum ou une suppression d'orphelins à sec lorsque le moteur le propose.
6. MERGE, évolution de schéma, voyage dans le temps
- MERGE : dédupliquez d'abord la source sur la clé de correspondance (les correspondances source en double échouent ou s'appliquent deux fois). Incluez un prédicat de partition ou de clustering dans la clause ON afin que le moteur élaguer les fichiers au lieu de scanner la table.
- Préférez l'append suivi d'un MERGE périodique ou d'une clé idempotente plutôt qu'un MERGE complet répété sur de grandes tables.
- Évolution de schéma : les colonnes additives sont sûres ; les renommages et l'élargissement de type dépendent du format et de la version (mappage de colonnes dans Delta, IDs de champ dans Iceberg). Vérifiez la prise en charge dans les lecteurs importants avant de renommer. N'activez jamais la fusion automatique de schéma sur une entrée de pipeline de production que vous ne contrôlez pas.
- Le voyage dans le temps est un outil de débogage, d'audit et de rollback, pas une sauvegarde. Il coûte les fichiers conservés ; plus la rétention est longue, plus le stockage est important et plus les métadonnées sont lentes. Définissez la rétention délibérément et conservez de vraies sauvegardes des tables critiques.
7. PySpark 4.x – Essentiels
- Les transformations sont paresseuses ; rien ne s'exécute avant une action. Construisez un plan, déclenchez une écriture ; des
count()oushow()répétés réexécutent tout le plan sauf si vous mettez en cache délibérément. - Évitez
collect(),toPandas()et.rddsur de grandes données ; ils ramènent tout au driver. Limitez ou agrégez d'abord. - Utilisez les fonctions intégrées ou les expressions SQL plutôt que les UDF Python. Les UDF bloquent les push-downs de l'optimiseur et sérialisent les lignes. Si inévitable, utilisez une UDF vectorisée (Arrow).
- Les opérations larges (join, groupBy, window, distinct) entraînent un shuffle. Filtrez et projetez avant elles ; vérifiez le plan avec
explain(). - Diffusez un petit côté d'une jointure (
broadcast(df)) lorsqu'il tient dans la mémoire de l'exécuteur ; ne diffusez pas une table non bornée. - L'exécution adaptative des requêtes (AQE) est activée par défaut dans Spark 4.x et gère la coalescence des partitions, les jointures asymétriques et le changement de stratégie de jointure ; ajustez après avoir lu le plan, pas avant.
- L'asymétrie se manifeste par une tâche lente ; salez ou isolez la clé chaude uniquement après que l'AQE s'avère insuffisante.
- Spark Connect est le mode client-serveur : le code s'exécute comme un client léger sur une session distante. Ne vous fiez pas aux éléments internes JVM locaux du driver (
sparkContext, API RDD) dans le code Connect. - Écrivez avec une taille de fichier cible explicite via la compaction, pas en repartitionnant avec un nombre deviné.
8. Approche locale d'abord
Développez et testez avec DuckDB ou Polars avant de payer pour un cluster.
- DuckDB lit Delta et Iceberg via des extensions et attache des catalogues DuckLake ; Polars lit Delta et Iceberg paresseusement avec push-down de prédicats.
- Utilisez un échantillon ou une partition sur la disposition réelle de la table pour que le comportement d'élagage de fichiers corresponde.
- Confirmez les sémantiques d'écriture (MERGE, vecteurs de suppression, fonctionnalités v3) sur le moteur réel ; les lecteurs locaux peuvent être en retard sur les fonctionnalités du format. Considérez une lecture locale réussie comme nécessaire, mais non suffisante.
Procédure
- Obtenez les chiffres : taille, croissance, écrivains, lecteurs, moteurs. 2. Parcourez la section 1 ; arrêtez-vous à l'option la moins chère qui convient. 3. Si lakehouse : choisissez le catalogue, puis le format (sections 4, 3). 4. Définissez la disposition et un planning de maintenance avant le premier chargement. 5. Prototypez localement (section 8), puis exécutez sur le cluster avec la sortie
explain()examinée. 6. Vérifiez avec les nombres de fichiers et les plans de requête, pas avec des suppositions.
Pièges connus et correctifs : voir references/gotchas.md.