Change Data Capture (CDC)
Résumé
Le CDC capte les changements d’une base source (insertions, mises à jour, suppressions) au lieu d’en recopier le contenu entier. Deux familles : l’interrogation périodique (on relit la table pour trouver ce qui a changé) et la lecture du journal de transactions de la base (log-based). La seconde capte tous les changements, y compris les suppressions, sans colonne « dernière modification » et avec peu de délai. Son revers : le flux peut renvoyer le même changement deux fois, donc le consommateur doit être idempotent.
Prérequis
ACID vs BASE (transaction) et Idempotence et backfill. Le journal de transactions est le fichier où la base note chaque changement avant de l’appliquer, pour pouvoir reprendre après une panne (WAL dans PostgreSQL, binlog dans MySQL).
Deux façons de détecter les changements
| Interrogation périodique (query-based) | Lecture du journal (log-based) | |
|---|---|---|
| Principe | Une requête cherche les lignes nouvelles ou modifiées | On lit le journal de transactions que la base écrit déjà |
| Colonne « dernière modification » | Obligatoire (updated_at) | Inutile |
| Suppressions | Invisibles : une ligne supprimée ne remonte plus | Capturées |
| Changements intermédiaires | Perdus si une ligne change deux fois entre deux requêtes | Tous capturés, dans l’ordre |
| Délai | La période d’interrogation | Faible (de l’ordre de la milliseconde pour MySQL ou PostgreSQL, d’après Debezium) |
| Charge sur la source | Requêtes répétées | Lecture du journal |
D’après la documentation de Debezium, le CDC par journal garantit la capture de tous les changements, ne demande aucune modification du modèle de données (pas de colonne de dernière modification), et peut capturer les suppressions ainsi que, selon la base, l’ancien état de la ligne et des métadonnées (identifiant de transaction, requête à l’origine).1
L’interrogation périodique reste correcte quand une propagation horaire suffit et que perdre des versions intermédiaires est acceptable.
Comment ça marche
flowchart LR DB[("Base source<br/>journal de transactions")] --> C["Connecteur CDC<br/>ex. Debezium"] --> K["Flux d'événements<br/>ex. Kafka"] --> T["Cible<br/>entrepôt, lakehouse"] DB -. "snapshot initial<br/>si les journaux anciens ont disparu" .-> C
- Debezium est un ensemble de connecteurs pour Kafka Connect : chaque connecteur lit les fonctions de capture de changements d’une base donnée (journal binaire MySQL, journal WAL de PostgreSQL, etc.) et publie chaque changement sur une ligne comme événement.1
- Snapshot initial. Si le connecteur démarre alors que la base a déjà jeté des journaux, il peut prendre une photo de l’état actuel avant de suivre les changements. Des snapshots incrémentaux, déclenchables à chaud, existent aussi.1
- Filtres et masquage. On peut inclure ou exclure schémas, tables et colonnes, et masquer des valeurs sensibles.1
PostgreSQL : logical decoding et slots
Dans PostgreSQL, le logical decoding extrait les changements persistants des tables dans un format compréhensible sans connaître le format interne du journal. Un replication slot garde la position d’un consommateur dans ce flux.2
- Un slot a un identifiant unique dans le cluster, persiste indépendamment de la connexion qui l’utilise et résiste aux pannes.
- En fonctionnement normal, un slot logique émet chaque changement une seule fois. Mais sa position n’est enregistrée qu’à chaque checkpoint : après une panne, le slot peut revenir en arrière et renvoyer des changements récents. La documentation précise que le client doit gérer ce cas pour éviter des effets indésirables, par exemple en mémorisant le dernier LSN vu.2
- Plusieurs slots indépendants peuvent exister sur une base, chacun avec son état : en général un slot par consommateur.2
Un slot oublié coûte du disque
Un slot qui n’est plus consommé oblige la base à conserver les journaux dont il a encore besoin, ce qui peut remplir le disque. Le paramètre
max_slot_wal_keep_sizelimite cette rétention ; sa valeur par défaut,-1, la laisse illimitée. Surveiller le retard des slots. Voir les replication slots (section « Replication Slots »).
Appliquer les changements dans la cible
Un événement de changement porte, en général, la clé de la ligne, le type d’opération (création, mise à jour, suppression) et les valeurs. Deux modèles pour la cible :
| Modèle | Principe | Usage |
|---|---|---|
| Miroir (type 1) | Appliquer chaque changement par MERGE : mettre à jour, insérer, supprimer | Une copie fidèle de la source |
| Historique (type 2) | Chaque changement ajoute une ligne datée | Garder le passé : Slowly Changing Dimensions, types 0 à 3 |
Un MERGE depuis un flux de changements doit :
- Garder le dernier événement par clé dans le lot (le plus récent, selon l’ordre du journal, pas l’heure de réception).
- Traiter les suppressions explicitement, sinon la ligne reste dans la cible.
- Ignorer un événement déjà appliqué, grâce au LSN ou à un numéro de séquence : c’est ce qui rend le consommateur idempotent.
MERGE dwh.clients AS t
USING (
SELECT * EXCEPT(rn) FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY client_id ORDER BY lsn DESC) AS rn
FROM staging.clients_changes
) WHERE rn = 1
) AS s
ON t.client_id = s.client_id
WHEN MATCHED AND s.op = 'd' AND s.lsn > t.last_lsn THEN DELETE
WHEN MATCHED AND COALESCE(s.op, 'c') <> 'd' AND s.lsn > t.last_lsn THEN
UPDATE SET ville = s.ville, last_lsn = s.lsn
WHEN NOT MATCHED AND COALESCE(s.op, 'c') <> 'd' THEN
INSERT (client_id, ville, last_lsn) VALUES (s.client_id, s.ville, s.lsn);Les noms de colonnes (op, lsn) sont illustratifs : adapter au format réel des événements.
Limite : pas de tombstone
Une fois la ligne supprimée de la cible, plus rien ne mémorise le LSN de la suppression. Un événement de création plus ancien qui arriverait après elle recréerait la ligne. Pour l’éviter, garder une table de suppressions (ou une colonne
deleted_atavec le dernier LSN) plutôt que supprimer physiquement.
CDC dans un lakehouse
Des mises à jour fréquentes et dispersées sont le cas typique du merge-on-read et exigent de la compaction : Maintenance d’un lakehouse et Data lake vs lakehouse.
Limites
- Schéma qui évolue. Un ajout ou un renommage de colonne dans la source doit être géré par le pipeline.
- Opérations en masse. Une mise à jour d’un million de lignes produit un million d’événements.
- Ordre entre tables. Le flux garantit un ordre par table ou par clé, pas toujours un ordre global.
- Sécurité. Le journal contient toutes les données : filtrer et masquer avant publication.
Notes liées
- Idempotence et backfill : appliquer deux fois le même événement.
- Slowly Changing Dimensions, types 0 à 3 : historiser les changements captés.
- ACID vs BASE : transactions et journaux.
- Maintenance d’un lakehouse : compaction des tables mises à jour souvent.
- Glossaire data engineering : CDC, idempotence.
- MOC Data Engineering : carte du vault.
Notes de bas de page
-
Debezium, Features, documentation consultée le 24 octobre 2026 (version 3.7), et Advantages of log-based CDC. ↩ ↩2 ↩3 ↩4
-
PostgreSQL 18, Logical Decoding Concepts, documentation consultée le 24 octobre 2026. ↩ ↩2 ↩3