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)
PrincipeUne requête cherche les lignes nouvelles ou modifiéesOn lit le journal de transactions que la base écrit déjà
Colonne « dernière modification »Obligatoire (updated_at)Inutile
SuppressionsInvisibles : une ligne supprimée ne remonte plusCapturées
Changements intermédiairesPerdus si une ligne change deux fois entre deux requêtesTous capturés, dans l’ordre
DélaiLa période d’interrogationFaible (de l’ordre de la milliseconde pour MySQL ou PostgreSQL, d’après Debezium)
Charge sur la sourceRequêtes répétéesLecture 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_size limite 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èlePrincipeUsage
Miroir (type 1)Appliquer chaque changement par MERGE : mettre à jour, insérer, supprimerUne copie fidèle de la source
Historique (type 2)Chaque changement ajoute une ligne datéeGarder le passé : Slowly Changing Dimensions, types 0 à 3

Un MERGE depuis un flux de changements doit :

  1. 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).
  2. Traiter les suppressions explicitement, sinon la ligne reste dans la cible.
  3. 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_at avec 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

Notes de bas de page

  1. Debezium, Features, documentation consultée le 24 octobre 2026 (version 3.7), et Advantages of log-based CDC. ↩ ↩2 ↩3 ↩4

  2. PostgreSQL 18, Logical Decoding Concepts, documentation consultée le 24 octobre 2026. ↩ ↩2 ↩3