Inhaltsverzeichnis16 Abschnitte
- TL;DR
- Die drei CDC-Patterns
- 1. Log-Based CDC
- 2. Trigger-Based CDC
- 3. Query-Based CDC (Polling)
- Debezium: Log-Based CDC, das tatsächlich shippt
- Debezium PostgreSQL-Connector-Konfiguration
- Die Debezium-Event-Struktur
- Deployen mit Kafka Connect
- Schema-Changes handhaben
- CDC vs. Full-Loads: Wann was?
- Common Pitfalls
- CDC-Daten explorieren, wenn sie landen
- FAQ
- Fazit
- Weiterlesen
Change Data Capture erklärt: Log-based CDC mit Debezium in Production
TL;DR
| Pattern | Latency | Source-Impact | Komplexität |
|---|---|---|---|
| Log-based (WAL / binlog) | Sekunden | Sehr niedrig | Hoch (Setup) |
| Trigger-based | Sekunden | Mittel | Mittel |
| Query-based (Polling) | Minuten | Hoch | Niedrig |
Log-based CDC ist der Gold-Standard für Production-Workloads. Die anderen sind Stepping-Stones oder Fallbacks.
Deine Operational-Database interessiert sich nicht für dein Warehouse. Jedes Mal wenn ein Customer sein Profile updatet, eine Order canceled oder die E-Mail ändert, lebt dieser State-Change in PostgreSQL oder MySQL — und dein Analytics-Layer ist sofort out-of-date. Change Data Capture (CDC) löst das, indem es Row-Level-Changes von einer Source-DB in Near-Real-Time an Downstream-Systems streamed.
Dieser Guide geht durch die drei CDC-Hauptpatterns, wie Debezium log-based CDC production-ready macht und wie du das in Kafka Connect wirst.
Die drei CDC-Patterns
1. Log-Based CDC
Jede major DB pflegt ein Transaction-Log für Crash-Recovery. PostgreSQL nennt es Write-Ahead-Log (WAL). MySQL nennt es Binary-Log (binlog). Diese Logs enthalten einen kompletten, geordneten Record jedes Row-Level-Changes — inklusive Deletes.
Log-based CDC tappt diesen Stream als sekundärer Consumer, ohne die Source-App zu modifizieren. Es liest das Transaction-Log, extrahiert Change-Events und forwardet downstream.
Advantages:
- Keine zusätzliche Load auf Source-Queries
- Captures Deletes und Updates, nicht nur Inserts
- Sub-Second-Latency erreichbar
- Keine App-Level-Changes nötig
Limitations:
- Braucht DB-Level-Privileges (Replication-Role)
- Log-Retention muss konfiguriert werden — Logs werden getrunkated
- Schema-Changes brauchen careful Handling downstream
PostgreSQL — Logical-Replication enablen:
-- postgresql.conf: set wal_level = logical (requires DB restart)
-- Create a replication slot für Debezium
SELECT pg_create_logical_replication_slot('debezium_slot', 'pgoutput');
-- Grant replication-Privileg an CDC-User
ALTER ROLE cdc_user REPLICATION LOGIN;
2. Trigger-Based CDC
DB-Triggers feuern auf INSERT, UPDATE oder DELETE und schreiben eine Kopie der changed Row in eine dedicated Changelog-Tabelle. Ein Downstream-Job pollt die Tabelle und verarbeitet neue Rows.
-- PostgreSQL: trigger-based CDC example
CREATE TABLE customers_changes (
change_id BIGSERIAL PRIMARY KEY,
change_type VARCHAR(10),
changed_at TIMESTAMPTZ DEFAULT NOW(),
old_data JSONB,
new_data JSONB
);
CREATE OR REPLACE FUNCTION capture_customer_changes()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'DELETE' THEN
INSERT INTO customers_changes (change_type, old_data)
VALUES ('DELETE', row_to_json(OLD)::jsonb);
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO customers_changes (change_type, old_data, new_data)
VALUES ('UPDATE', row_to_json(OLD)::jsonb, row_to_json(NEW)::jsonb);
ELSE
INSERT INTO customers_changes (change_type, new_data)
VALUES ('INSERT', row_to_json(NEW)::jsonb);
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_customers_cdc
AFTER INSERT OR UPDATE OR DELETE ON customers
FOR EACH ROW EXECUTE FUNCTION capture_customer_changes();
Trigger-based CDC funktioniert, aber jeder Write zur Source-Table kostet Trigger-Overhead. Die Changelog-Tabelle braucht auch regular Pruning, und der Ansatz skaliert nicht graceful unter High-Write-Loads.
3. Query-Based CDC (Polling)
Der einfachste Approach: alle N Minuten eine Query laufen lassen, die Rows seit letztem Checkpoint mit updated_at-Timestamp-Column selectet.
-- Query-based CDC: fetch rows changed since last run
-- Standard SQL — works on PostgreSQL, MySQL, and most other RDBMS
SELECT *
FROM customers
WHERE updated_at > '2024-01-15 10:00:00'
ORDER BY updated_at ASC;
Das fundamentale Problem: Hard-Deletes sind unsichtbar. Wenn eine Row gelöscht wird, gibt es kein updated_at zum Tracken. Soft-Deletes (eine deleted_at-Column) lösen das partiell, brauchen aber Disziplin über die ganze App.
Query-based CDC ist okay für kleine Tables, infrequent Updates oder Environments, wo du die Source-Schema kontrollierst. Für High-Volume-Production ein Kompromiss.
Debezium: Log-Based CDC, das tatsächlich shippt
Debezium ist eine OSS-CDC-Plattform auf Apache Kafka. Liest Transaction-Logs von PostgreSQL, MySQL, MongoDB, SQL Server, Oracle und mehr — und emittiert strukturierte Change-Events an Kafka-Topics.
Jedes Event enthält den vollen Before/After-State der Row, Operation-Type (c, u, d, r) und Metadata: Source-Table, Transaction-ID, LSN, Wall-Clock-Timestamp. Reicher und reliable als alles, was Polling-based produzieren kann.
Debezium PostgreSQL-Connector-Konfiguration
{
"name": "customers-postgres-connector",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"database.hostname": "postgres-host",
"database.port": "5432",
"database.user": "cdc_user",
"database.password": "your_password",
"database.dbname": "production_db",
"database.server.name": "prod",
"table.include.list": "public.customers,public.orders",
"plugin.name": "pgoutput",
"slot.name": "debezium_slot",
"publication.name": "debezium_publication",
"topic.prefix": "prod",
"snapshot.mode": "initial"
}
}
Produziert Kafka-Topics prod.public.customers und prod.public.orders. Jeder Insert, Update, Delete auf diesen Tables wird zur Kafka-Message.
Die Debezium-Event-Struktur
{
"op": "u",
"before": {
"id": 42,
"email": "old@example.com",
"updated_at": 1705312800000
},
"after": {
"id": 42,
"email": "new@example.com",
"updated_at": 1705316400000
},
"source": {
"version": "2.5.0.Final",
"connector": "postgresql",
"name": "prod",
"ts_ms": 1705316400123,
"db": "production_db",
"schema": "public",
"table": "customers",
"txId": 1234567,
"lsn": 987654321
}
}
op-Werte: c = create (Insert), u = update, d = delete, r = read (aus initialem Snapshot).
Deployen mit Kafka Connect
Debezium läuft als Kafka-Connect-Source-Connector. Wenn du Kafka Connect schon operiert, ist Debezium-Deployment straightforward: Connector-JAR zum Connect-Plugin-Path adden und Config an Connect-REST-API POSTen.
# Connector via Kafka Connect REST-API registrieren
curl -X POST http://kafka-connect:8083/connectors \
-H "Content-Type: application/json" \
-d @customers-postgres-connector.json
# Connector-Status checken
curl http://kafka-connect:8083/connectors/customers-postgres-connector/status
Vom Kafka-Topic kannst du Change-Events zu jedem Sink routen: Object-Storage (S3, GCS), Warehouse (Snowflake, BigQuery) oder Elasticsearch. Confluent Hub liefert ready-made Sink-Connectors für die meisten Targets.
Schema-Changes handhaben
Die trickigste Long-Term-Challenge ist Schema-Evolution. Wenn das Source-Team eine Column addet, müssen CDC-Consumer alte und neue Event-Formate handhaben ohne zu failen.
Debezium integriert mit Confluent Schema Registry (oder kompatiblen Alternativen wie Karapace), um Avro-, Protobuf- oder JSON-Schema-Evolution zu managen. Mit BACKWARD- oder FULL-Compatibility-Mode ist Adden von Nullable Columns safe. Dropping oder Renaming Columns ist immer breaking.
Best Practice: Source-Schema-Changes als Deployment-Event behandeln. Schema-Registry-Compatibility-Checks in CI-Pipeline nutzen und Changes mit Downstream-Consumer koordinieren, bevor Rollout. Debezium mit einem Data-Contract-Prozess paaren schließt den Loop properly.
CDC vs. Full-Loads: Wann was?
| Szenario | CDC | Full-Load |
|---|---|---|
| High-Volume-OLTP (>1M Changes/Tag) | Ja | Nein, zu langsam |
| Kleine Reference-Tables (<10k Rows) | Overkill | Ja, einfach |
| Hard-Deletes capturen | Ja | Nein, unsichtbar |
| Source-DB ohne Replication-Support | Nein | Ja |
| Near-Real-Time-Latency (<1 min) | Ja | Nein |
| Team ohne Kafka-Infra | Nein, komplex | Ja |
Common Pitfalls
WAL-Retention zu kurz. Wenn Debezium-Connector länger offline ist als das Log-Retention-Window, verliert er seine Position und muss re-snapshotten. wal_keep_size (PostgreSQL) generös setzen und Replication-Slot-Lag continuously monitoren.
Initial-Snapshot nicht planen. Bevor Streaming neuer Changes nimmt Debezium einen konsistenten Snapshot existierender Daten. Bei großen Tables kann das Stunden laufen. Planen — und Snapshot validieren, bevor Real-Time-Streaming enablen.
Tombstone-Events ignorieren. Nach Emit eines Delete-Events publisht Debezium eine Null-Value-Tombstone-Message zum selben Key. Manche Consumer failen silently. Explizit handhaben.
Null-Before-State bei Inserts. Für op: c ist before immer null. Immer op checken, bevor before zugreifen.
Compacted Topics removen Deletes. Bei log-compacted Kafka-Topics verschwinden Tombstone-Messages schließlich. Für CDC-Streams, die volle History replayen müssen, Retention-based (nicht Compaction-based) Topics nutzen.
CDC-Daten explorieren, wenn sie landen
Wenn dein CDC-Stream als Parquet oder CSV in Object-Storage landet, musst du noch Current-State rekonstruieren — Inserts, Updates und Deletes in Sequence applyen. Tools wie dbt handhaben das mit Snapshot-Models. Für Ad-Hoc-Validation — "hat dieser Record heute geändert? Wie sah er vorher aus?" — willst du den Raw-Change-Log schnell queryen ohne Infra-Spin-Up.
Harbinger Explorer lässt dich Parquet oder CSV uploaden und direkt im Browser mit DuckDB WASM queryen. In Deutsch fragen: "Zeige alle Rows, wo op gleich u und customer_id gleich 42" — die AI generiert SQL gegen dein tatsächliches Schema. Schneller Weg, CDC-Correctness während Dev zu validieren.
FAQ
Was passiert, wenn der Source-Server crasht während CDC läuft? Debezium pflegt seine Position via Replication-Slot. Nach Recovery liest er ab letzter Position weiter — keine Datenverlust solang WAL-Retention reicht.
Wie monitor ich Replication-Lag?
PostgreSQL: SELECT * FROM pg_replication_slots; zeigt confirmed_flush_lsn vs. pg_current_wal_lsn(). Bei großem Delta hat dein Consumer Lag. Prometheus-Exporter dafür ist verfügbar.
Können mehrere CDC-Consumer dieselbe Source nutzen? Ja, aber jeder braucht eigenen Replication-Slot. Slots akkumulieren WAL, also alle aktiv halten oder Slots droppen, sonst läuft die Disk voll.
Was bei Schema-Changes auf der Source-DB?
Debezium captures DDL-Events in Schema-History-Topic. Bei BACKWARD-Compatibility addst du Nullable-Columns safe. Type-Changes brauchen Coordination — Source und Consumer simultan deployen.
Fazit
CDC — speziell log-based CDC via Debezium — ist der zuverlässigste Weg, Downstream-Systems mit Operational-Datenbanken in Real-Time zu synchronisieren. Nicht trivial zu setupen, aber für High-Volume-Latency-sensitive Pipelines das richtige Tool. Mit einer Table starten, Event-Format End-to-End validieren, für Schema-Changes planen und ausweiten.
Zum Strukturieren der CDC-Daten, die in deinem Lakehouse landen, lies den Medallion-Architecture-Guide — er zeigt, wie Raw-Change-Events in eine Bronze → Silver → Gold-Pipeline passen.
Weiterlesen
- Medallion-Architecture erklärt
- ETL vs. ELT: Welches Pipeline-Pattern passt zu deinem Stack?
- Data-Quality-Testing in Pipelines
Stand: 14. Mai 2026.
Geschrieben von
Harbinger Team
Cloud-, Data- und AI-Engineer in DACH. Schreibt seit 2018 über infrastrukturkritische Tech-Entscheidungen — keine Marketing- Folien, sondern echte Trade-offs aus Production-Workloads.
Hat dir das geholfen?
Jede Woche ein neuer Artikel über DACH-Cloud, Data und AI — direkt in dein Postfach. Kein Spam, kein Marketing-Sprech.
Kein Spam. 1-Klick-Abmeldung. Datenschutz bei Loops.so.