Parquet, Delta Lake, CSV, colonnes contre lignes, partitionnement et petits fichiers : ce qu’on demande sur la couche de stockage d’un lac de données.
Cliquez sur une question pour dérouler la réponse attendue.
Parce que le lac sert surtout des lectures analytiques : on filtre et on agrège quelques colonnes sur beaucoup de lignes. Un format colonnes (Parquet, ORC) pose chaque colonne dans son propre flux, donc le moteur ne télécharge et ne décompresse que ce qu’il projette. Un format lignes (Avro, JSON, CSV) force à lire l’enregistrement entier pour extraire trois champs.
C’est un argument d’entrée-sortie, pas de confort. Sur un objet de 800 Mo à 200 colonnes, une requête qui n’en touche que quatre peut lire quelques dizaines de Mo au lieu du fichier entier. Le facteur se retrouve sur la facture du stockage objet et sur le temps d’attente, quel que soit le moteur — Spark, Trino, DuckDB ou Athena.
Le colonnes gagne aussi sur la compression : des valeurs d’un même type, souvent répétées, se compressent ensemble. Et il porte des statistiques par bloc (min, max, nulls) qui permettent de sauter des plages sans les ouvrir.
Que vous ne récitez pas « Parquet c’est mieux ». Vous savez quand le colonnes est le mauvais choix. Un flux qui relit toujours la ligne complète — CDC, file d’événements, reprise de streaming — n’a aucun bénéfice à projeter. Là, un format lignes (Avro) est plus naturel.
La phrase qui clôt : le lac stocke pour être relu sélectivement ; le format suit ce contrat.
Un row group est une tranche horizontale du fichier Parquet : un paquet de lignes — souvent 64 Mo à 1 Go non compressés — dans lequel chaque colonne est stockée à part, avec son propre mini-pied (min, max, nombre de nulls, encodage). Le fichier n’est pas une seule colonne géante : c’est une suite de ces tranches.
Ça compte pour trois raisons concrètes.
Que vous ne confondez pas row group, page et fichier. La page (souvent 1 Mo) est le grain de compression et de dictionnaire à l’intérieur d’une colonne. Le fichier est l’objet S3. Le row group est le compromis entre les deux.
Le chiffre à avoir en tête : viser des fichiers de 128 Mo à 1 Go avec des row groups assez larges pour que les stats servent, assez étroits pour rester parallélisables. Un Parquet de 20 ko n’a, en pratique, qu’un row group inutile.
Deux mécanismes, deux économies, souvent confondus.
La projection (column pruning) dit : « je n’ai besoin que de ces colonnes ». Le lecteur n’ouvre que les flux concernés dans chaque row group. C’est mécanique, et ça marche dès que le format est colonnes : une requête sur date et montant dans une table à 80 champs ne lit que deux colonnes.
Le prédicat (predicate pushdown) dit : « je n’ai besoin que des lignes qui passent ce filtre ». Le lecteur consulte les statistiques du row group (min, max, nulls). Si date y va de janvier à mars et que le filtre demande date >= 2026-07-01, le groupe entier est sauté — sans décompresser. Si la plage chevauche, le groupe est lu, puis filtré en mémoire.
Que vous savez que le prédicat n’est pas une garantie. Des dates mélangées dans chaque fichier — un historique réécrit sans clustering — rendent min et max trop larges : plus aucun groupe n’est sautable. La projection, elle, continue de marcher.
La relance classique : « et un LIKE '%foo%' ? » Le min/max ne sert à rien sur un motif au milieu d’une chaîne. Le moteur lira la colonne, éventuellement toute, même en Parquet.
Formule à retenir : la projection économise des colonnes, le prédicat économise des blocs — à condition que les données soient rangées.
À réduire le volume avant même le codec (Snappy, Zstd). Ce ne sont pas des algorithmes de compression généraux : ce sont des encodages de colonne, qui exploitent la répétition.
Le dictionnaire remplace chaque valeur par un petit entier. Une colonne pays à 40 modalités distinctes dans un row group devient une table de 40 chaînes plus un flux d’indices. Sur une colonne à faible cardinalité, le gain est énorme. Sur un identifiant unique par ligne, le dictionnaire grossit autant que les données : Parquet le désactive au-delà d’un seuil (souvent quand le dictionnaire dépasse une page).
Le RLE (run-length encoding) compacte les suites identiques : « France, 12 000 fois ». Il est très efficace après un tri ou un clustering, et presque inutile si les valeurs sont mélangées.
Les deux se combinent souvent : dictionnaire, puis RLE sur les indices. C’est pour ça qu’une colonne catégorielle bien groupée pèse dix fois moins qu’un JSON équivalent.
Que vous reliez encodage et disposition. Un OPTIMIZE ZORDER BY pays n’accélère pas seulement le skip : il allonge les runs, donc le RLE. À l’inverse, des petits fichiers n’ont pas assez de valeurs pour qu’un dictionnaire soit rentable.
Ne dites pas « Parquet compressé donc plus petit » : dites encodage de colonne, puis codec, et citez un cas où ça échoue (haute cardinalité, valeurs uniques).
Les deux sont colonnes, avec statistiques, encodages et compression. En entretien, le choix n’est pas « lequel est plus rapide », c’est quel écosystème vous devez servir.
Parquet est le format d’échange du lac moderne : Spark, Trino, DuckDB, pandas, Athena, BigQuery, Snowflake (lecture externe), Flink. Un fichier Parquet écrit aujourd’hui reste lisible demain par un moteur que vous n’avez pas encore choisi. C’est l’argument qui pèse le plus.
ORC reste très fort dans le monde Hive / Tez / Impala historique : index plus riches, indexes de bloom plus anciens, parfois une meilleure tenue sur des types Hive complexes. Si tout le parc lit via Hive et que personne n’emporte les fichiers ailleurs, ORC n’est pas une erreur.
Trois critères concrets pour trancher :
Que vous ne partez pas en benchmark de blog. Le piège est de comparer un ORC Hive 1.2 à un Parquet Spark 3.5 : vous mesurez la pile, pas le format.
Réponse saine : Parquet par défaut, ORC seulement si le parc le dicte.
Quand vous relisez la ligne entière, souvent, et que le contrat qui compte est le schéma évolutif du producteur, pas la projection analytique.
Avro est un format lignes, avec le schéma dans le fichier (ou via un schema registry). Écrire un événement, le relire tel quel, le republier : le coût est prévisible. Ajouter un champ optionnel, retirer un champ avec une règle de compatibilité (BACKWARD, FORWARD) : c’est le quotidien de Kafka, du CDC, des files d’ingestion.
Parquet est le contraire : excellent pour scanner trois colonnes sur un an, médiocre comme format de buffer entre deux services. Un producteur qui émet des micro-lots Parquet crée des petits fichiers, paie un pied de fichier lourd, et n’offre aucun bénéfice de colonnes si le consommateur désérialise tout.
Les cas où Avro gagne, à citer :
Que vous ne dites pas « Avro c’est vieux ». Vous distinguez couche d’échange / de log et couche de stockage analytique. Le motif sain : Avro (ou JSONL) à l’arrivée, conversion une fois vers Parquet ou une table Iceberg/Delta, puis plus jamais de relecture du brut pour du reporting.
Le contre-exemple à avoir : une table de faits de 400 colonnes relue par des analystes n’a rien à faire en Avro.
Parce que JSON décrit un document, pas une table scannable. Chaque ligne (ou chaque objet) répète les noms de champs, ignore les types stables, et se lit de gauche à droite. Aucune projection, aucun prédicat sur statistiques, aucune compression colonnes. Vous payez le volume du texte et le parse de chaque enregistrement, à chaque requête.
JSONL (un objet par ligne) est un peu moins pire que le JSON tableau unique : au moins le fichier est découpable. Ce n’est toujours pas un format de lac. C’est un format d’échange ou de journal — API, dumps d’application, zone brute.
Les ennuis concrets qui sortent en entretien :
Que vous ne diabolisez pas le JSON : il est légitime à la frontière. La règle : on l’accepte une fois, on le valide contre un contrat (Avro, JSON Schema), on le matérialise en colonnes. Garder du JSONL comme table de faits « parce que c’est souple » est exactement ce qui rend un lac illisible six mois plus tard.
Le CSV n’a pas de schéma. Tout le reste en découle. En entretien, on ne vous demande plus « pourquoi Parquet » — c’est déjà ailleurs. On vérifie que vous avez déjà cassé un chargement CSV.
Les pièges qui reviennent :
0012 est une chaîne. Un lecteur trop zélé en fait un entier et perd les zéros. Un N/A dans une colonne d’âges bascule tout en texte, et "9" > "18" devient vrai.03/04/2026 est le 3 avril ou le 4 mars selon l’émetteur.Que votre réflexe n’est pas « j’infère le schéma ». C’est : contrat explicite (types, séparateur, encodage, nulls), rejet des lignes hors contrat, conversion une fois vers un format colonnes. Le CSV reste un format d’échange, pas une table.
Les deux, avec des rôles distincts : le fichier dit ce qu’il contient vraiment, le catalog dit comment on l’appelle et ce qu’on a le droit d’y écrire.
Lire la réponse détailléePas « une colonne en plus ». Ajouter un champ nullable est l’évolution la plus sûre : les vieux fichiers n’ont pas la colonne, le lecteur pose des nulls. Ce qui casse, ce sont les changements non additifs.
Les cas à citer, dans l’ordre où on les voit :
INT vers STRING, TIMESTAMP vers DATE, DECIMAL(10,2) vers DOUBLE. Selon le moteur : erreur franche, ou conversion silencieuse qui arrondit les montants.client_id et id_client sont deux colonnes. Les anciennes valeurs deviennent nulles, les nouvelles aussi sur l’historique. Iceberg sait suivre un column id ; Parquet nu, non.LONG dans une colonne cataloguée INT, ou une struct à un champ de plus lu par un écrivain vieux qui réécrit tout le type.Que vous parlez de politique (Delta mergeSchema, Iceberg schema.update, compatibilité Avro), pas de chance. La relance : « comment vous ajoutez une colonne en production ? » Réponse attendue : job de migration versionné, écriture validée contre le catalog, lecture pinée sur une version le temps du bascule — pas un refresh en priant.
On partitionne sur ce que presque toutes les lectures filtrent, à une cardinalité raisonnable, et qui sert aussi à expirer les données — rarement sur un identifiant.
Lire la réponse détailléeParce qu’une partition n’est pas un index : c’est un dossier d’objets. Un user_id à dix millions de valeurs, c’est dix millions de préfixes. Chaque écriture pose un (ou plusieurs) fichiers minuscules dans un dossier presque vide. Vous n’avez plus une table : vous avez un listing S3.
Trois factures, toutes payées avant le calcul :
WHERE user_id IN (8 millions d’id). On filtre un jour, un pays, un type d’événement. L’identifiant sert une jointure ou un point lookup — ce n’est pas le rôle d’un dossier Hive.Que vous proposez autre chose que « alors on ne range pas ». Pour un accès par id : table de faits partitionnée par date, plus clustering sur l’id, ou un magasin clé-valeur à côté. Pour un WHERE user_id = ? fréquent et pointu, un lac colonnes n’est peut-être pas le bon système.
La phrase nette : haute cardinalité = clustering ou index, pas partition.
Ça range les valeurs proches dans les mêmes fichiers, donc les statistiques min-max redeviennent discriminantes et le moteur saute des blocs — sans exploser le nombre de dossiers.
Lire la réponse détailléeTrop d’écrivains, trop de partitions, trop de micro-lots : le lac se retrouve avec des millions d’objets. On compacte, on vise 128 Mo à 1 Go, et on arrête de découper plus fin que le volume.
Lire la réponse détailléeParce que l’objet devient l’unité de parallèle et de réécriture. Un Parquet de 80 Go, même bien colonnes, se découpe au row group : trop peu de groupes, ou des groupes énormes, et une poignée de lecteurs portent tout le scan. Un nœud lent, un retry, et vous relisez des dizaines de Go.
Pire : une mise à jour locale. En copy-on-write (Delta, Iceberg par défaut souvent), un MERGE qui touche 0,1 % des lignes réécrit le fichier entier. Un objet de 20 Go pour corriger 200 clés, c’est la facture qu’on découvre en production. En merge-on-read, le fichier reste, mais les delete files s’empilent jusqu’à une compaction qui, elle, paiera la réécriture.
Les autres ennuis : un row group trop large tient mal en mémoire ; un objet qui dépasse les seuils de certaines couches (limites de split, timeouts de GET) échoue de façon opaque ; le restore d’un snapshot devient un monstre.
Que « plus gros = mieux » n’est pas votre règle. La cible usuelle reste 128 Mo à 1 Go compressés, assez pour amortir le footer et le listing, assez petite pour réécrire et paralléliser. Au-delà, on découpe (rewrite, bin-pack inverse), on n’admire pas le fichier unique.
Cas limite acceptable : une table append-only, lue rarement, jamais mergée — un très gros objet dérange moins. Dès qu’il y a upsert ou time travel fin, la taille redevient un levier opérationnel.
Les données restent du Parquet ; le dossier delta log enchaîne des commits versionnés. Un lecteur ne voit qu’un snapshot fermé, une écriture concurrente se résout par conflit de version, pas par dossier à moitié rempli.
Lire la réponse détailléeIl sert à relire un snapshot : VERSION AS OF ou TIMESTAMP AS OF. Trois usages qui passent en entretien : auditer ce qu’un job a vu hier ; rejouer un calcul après un mauvais chargement sans restaurer tout le bucket ; comparer deux versions (un EXCEPT entre v41 et v42) pour comprendre un MERGE.
Ce n’est pas une machine à remonter le temps illimitée. Chaque version retenue empêche d’effacer les fichiers que cette version référence encore. Le coût est donc du stockage (anciens Parquet + log) et, parfois, de la complexité : un lecteur piné sur une vieille version voit un schéma ancien.
La durée utile dépend de la rétention (delta.deletedFileRetentionDuration, souvent 7 jours par défaut sur beaucoup de plateformes) et de VACUUM. Au-delà, la version n’est plus matériellement là.
Que vous ne vendez pas le time travel comme un sauvegarde réglementaire. Sept jours de snapshots ≠ politique de backup, ≠ PRA hors région. Pour un audit long, on copie un snapshot (table clone, export) hors de la fenêtre de VACUUM.
La relance : « ça ralentit les lectures courantes ? » Non, le courant lit le dernier checkpoint. Ça gonfle la facture objet et allonge VACUUM. Un MERGE toutes les minutes pendant un mois, sans compaction ni expiration, laisse une forêt de tombstones et d’anciens fichiers.
VACUUM supprime les fichiers d’objets que plus aucune version dans la fenêtre de rétention ne référence. Après ça, VERSION AS OF trop ancien échoue, ou — pire selon l’outil — renvoie un état incomplet si quelqu’un a contourné le protocole. Le piège : on active le time travel dans les démos, puis un cron VACUUM à 24 h pour « faire le ménage », et l’équipe finance ne peut plus expliquer le chiffre de mardi.
Deuxième piège, inverse : ne jamais vacuum. Les MERGE et OPTIMIZE laissent les anciens Parquet. Le bucket double tous les trimestres, les listings ralentissent, et personne ne relie ça au journal.
La règle opérationnelle : rétention ≥ le plus long besoin de relecture (rejeu d’un job quotidien : 3–7 jours ; enquête fraude : beaucoup plus, donc clone ou backup, pas 90 jours de VACUUM retardé sur la table chaude). Et on ne vacuum pas en dessous du délai où un lecteur long (un streaming ouvert, un notebook oublié) tient encore un snapshot.
Que vous citez le seuil de sécurité historique de Delta (souvent 7 jours) et que vous savez pourquoi VACUUM … RETAIN 0 HOURS est une arme : ça casse les lecteurs concurrents et le time travel d’un coup. Sur Iceberg, l’équivalent est l’expiration de snapshots + delete orphan files : même tension, autres noms.
Phrase attendue : time travel et VACUUM sont le même levier, pas deux fonctionnalités indépendantes.
Quand une clé métier doit rester unique face à des arrivées tardives ou des corrections. Le prix est une réécriture de fichiers, pas une mise à jour de ligne : on s’en sert par lots, pas comme un UPDATE OLTP.
Lire la réponse détailléeOn choisit d’abord l’écosystème de moteurs et le catalog, ensuite le type d’écriture (append, upsert massif, CDC). Le format de fichiers en dessous est presque toujours du Parquet.
Lire la réponse détailléeLe fichier dit comment lire un objet. Le catalog dit quelle table on interroge, où elle vit, qui y a droit, et quels fichiers en font partie — surtout dès que plusieurs moteurs partagent le lac.
Lire la réponse détailléeUn codec après l’encodage de colonnes. Le compromis est toujours le même : ratio contre CPU contre découpabilité.
Snappy (et l’équivalent LZ4) : très rapide à compresser et décompresser, ratio moyen, splittable dans Parquet (on décompresse page par page). C’est le défaut historique de beaucoup de lacs : on privilégie le temps de scan.
Zstd : souvent le meilleur équilibre aujourd’hui — meilleur ratio que Snappy, décompression encore vive, niveaux réglables. De plus en plus le choix par défaut quand la plateforme le propose.
Gzip : meilleur ratio (surtout en niveau élevé), CPU cher, et un fichier gzip « nu » (CSV.gz, JSON.gz) n’est en général pas découpable : un seul lecteur pour tout l’objet. En page Parquet, Gzip reste splittable au grain de la page, mais reste plus lent. On le réserve à de l’archive froide rarement relue.
Que vous séparez codec dans Parquet et objet .gz. Le second est le piège classique d’ingestion. Et que vous ne changez pas de codec « pour gagner 8 % » sans mesurer le scan : un Zstd un peu plus petit mais plus long à décoder peut perdre sur un cluster déjà CPU-bound.
Réponse saine : Zstd ou Snappy pour les tables chaudes, Gzip (ou glacé) pour l’archive, jamais de CSV.gz comme table de faits.
À ne pas ouvrir les objets inutiles. Chaque fichier (et chaque row group) publie, pour les colonnes intéressantes, un min, un max, un compte de nulls, parfois un bloom filter ou des histogrammes. Le planificateur, avant les GET de données, compare ces bornes au WHERE. C’est le data skipping : la même idée que le predicate pushdown, remontée au manifeste de table (Delta, Iceberg) pour ne même pas considérer le fichier.
Sans stats, ou avec des stats trop larges (valeurs mélangées), le moteur retombe sur un scan. D’où l’intérêt du clustering : resserrer les bornes. D’où aussi le coût des petits fichiers : des milliers de pieds à lire, chacun avec des stats médiocres.
Iceberg pose ces stats dans les manifests ; Delta dans le log et les data skipping indexes / footers. Le lecteur SQL n’a pas à ouvrir 50 000 Parquet pour savoir lesquels touchent dt = 2026-09-13 si dt est aussi une partition — et s’il ne l’est pas, les stats sont le plan B.
Que vous savez quand elles mentent. Une colonne de type string mal parsée, un timestamp en plusieurs fuseaux, un float de montant : les bornes existent et ne servent à rien. Ou pire : stats absentes sur une colonne ajoutée après coup, jamais backfill.
Relance : « ça suffit sans partition ? » Sur une petite table, oui. Sur un pétaoctet, les stats dans les manifests évitent le listing aveugle, mais une partition (ou un transform Iceberg) reste le premier couteau.
Trois contrats d’écriture, trois façons de perdre des données si on se trompe.
Append. On ajoute des fichiers (et un commit). L’historique déjà publié reste. C’est le défaut sain pour des faits immuables. Le risque : doublons si on rejoue le même lot sans déduplication ni MERGE.
Overwrite (table entière). Le nouveau snapshot remplace tous les fichiers. Utile pour une dimension recalculée de zéro, ou un datamart quotidien. Le risque : un job qui n’écrit que janvier efface février à décembre. En Parquet nu sans journal, un overwrite mal fait laisse un dossier à moitié vidé, visible tout de suite.
Dynamic overwrite (overwrite des partitions présentes dans le lot). On réécrit dt=2026-09-13 sans toucher aux autres jours. C’est le mode des rejeux : « je recalcule la journée, pas le lac ». Le risque : une colonne de partition mal calculée (un null, un mauvais fuseau) crée une partition poubelle et/ou écrase la mauvaise feuille.
Que vous liez le mode au grain de rejeu. Relancer un lot horaire en overwrite global est une erreur de débutant. Relancer en append sans idempotence aussi. La phrase nette : append si l’événement est unique, overwrite dynamique si la partition est le grain de vérité, overwrite total seulement si la table entière est le résultat.
D’abord on distingue date d’événement et date d’ingestion. Une ligne du 10 arrive le 14 : si vous n’avez écrit que dans dt=2026-09-14 (ingestion), les requêtes « journée du 10 » sont fausses. Si vous écrivez dans dt=2026-09-10, vous devez rouvrir une partition fermée.
Les stratégies, à citer sans en faire un cours :
Le watermark de streaming n’efface pas le problème métier : il dit seulement ce que le job refuse de réouvrir. Au-delà, c’est un process (rejeu, job de rattrapage), pas un paramètre.
Que vous ne répondez pas « on partitionne sur l’ingestion, donc pas de souci ». C’est un souci déplacé vers tous les dashboards. Et que vous chiffrez la fenêtre : 2 h, 2 jours, 2 semaines changent le layout et le budget de réécriture.
Deux couches que le format de table ne remplace pas.
Chiffrement. Au repos, presque toujours par le object store (SSE-S3, SSE-KMS, clés gérées). KMS donne rotation, audit, et la possibilité de révoquer un accès sans tout réécrire. Le chiffrement côté client (le job chiffre avant le PUT) existe ; il casse souvent le listing, les stats, et les services de compaction qui ne voient plus du Parquet. En transit : TLS, évident, à vérifier sur les endpoints privés.
Une table Delta/Iceberg chiffrée fichier par fichier avec des clés différentes, sans le support de la plateforme, rend OPTIMIZE et les lecteurs tiers… intéressants. On aligne une politique (bucket + KMS + grants catalog).
Cycle de vie. Transitions (Standard → IA → Glacier) et expirations. Le piège : un lifecycle qui efface des objets encore référencés par un snapshot, ou qui glace des fichiers que VACUUM n’a pas encore le droit de considérer morts. Inversement, garder Intelligent-Tiering sur une table chaude compactée toutes les heures coûte des retrievals. On couple rétention de table (snapshots, VACUUM) et règles bucket : même durée, mêmes préfixes, exception pour _delta_log / metadata qu’on n’envoie pas au Glacier à la légère.
Que vous parlez préfixe et snapshots, pas seulement « on a activé le chiffrement ». Un lac « sécurisé » dont le lifecycle détruit le log est un incident. Un lac chiffré dont tout le monde a s3:* sur le bucket est un théâtre.
Ce n’est pas le même contrat de fichier.
Le streaming produit souvent : petits lots, schéma contraint par un registry, besoin de reprendre à un offset. Avro (ou Protobuf) sur une file, puis écriture en table (Iceberg/Delta) avec roll de fichiers, voilà le motif sain. Écrire du Parquet à chaque micro-lot de 200 ko sans compaction, c’est fabriquer le lac illisible. JSONL en topic « pour aller vite » se paie au parse et au schéma mou.
Le batch (rejeu quotidien, backfill, datamart) vise des gros objets colonnes, stats utiles, éventuellement overwrite dynamique d’une partition. Parquet / ORC dans une table transactionnelle. Relire le topic Avro de six mois pour un dashboard est un aveu : la couche batch n’existe pas.
Les deux se rencontrent dans la table. Le stream commite des snapshots ; le batch lit le dernier (ou un pin). Ce n’est pas « Parquet versus Kafka » : Kafka n’est pas un lac. C’est file d’échange + table de stockage.
Que vous refusez « on met du Parquet partout » autant que « on garde du JSONL parce que c’est le stream ». Et que vous nommez le compactage comme partie du chemin streaming, pas comme un luxe batch du dimanche.
En colonnes, une table large n’est pas une table lignes qui a grossi. Ajouter 80 colonnes rarement lues coûte surtout du schéma et un peu de footer, pas 80 fois le scan — si les lecteurs projettent. C’est pour ça qu’un lac tolère des faits très larges (une ligne d’événement avec des attributs) mieux qu’un OLTP.
La facture revient autrement :
L’étroite (étoile : faits minces + dimensions) simplifie les MERGE de dimensions et les droits. Elle coûte des jointures. En lac, une jointure large tous les matins peut valoir plus cher que trois colonnes de trop dans le fait — à mesurer.
Que vous ne récitez pas « 3NF partout ». Vous parlez projection, réécriture, consommation. Large pour des faits append-only très lus par colonnes ; étroit quand l’upsert et la gouvernance de colonnes dominent.
On mesure d’abord le planning et le listing, pas le CPU : explosion d’objets, schémas qui divergent, partitions vides, et des lectures qui n’osent plus passer par le catalog.
Lire la réponse détailléeOn n’entrepose pas dans le format avec lequel on s’échange.
L’échange (API, export métier, topic, pièce jointe) privilégie ce que l’autre système sait lire : CSV, JSON, Avro, Excel. C’est ponctuel, souvent verbeux, souvent sans stats. Le stockage d’un lac privilégie ce qu’on va relire cent fois : colonnes, pied de fichier, commits, catalog. On convertit à la frontière, une fois, avec un schéma explicite, puis les jobs internes ne revoient plus le CSV.
La phrase inverse est aussi vraie : on ne sort pas un datamart en posant un préfixe Parquet sur un partenaire qui attend un CSV horodaté. On exporte depuis la table, on n’invite pas le partenaire dans le _delta_log.
Que vous avez une ligne : brut d’échange → validation → table colonnes (Iceberg/Delta/Parquet tenu) → export éventuel. Pas une zone raw qui est devenue le reporting. C’est la même idée que « le CSV n’est pas un format de stockage », généralisée à JSON, Excel, et aux dumps applicatifs.
Si vous ne dites qu’une phrase dans tout l’entretien formats : échange à la porte, colonnes et catalog à l’intérieur.