Change Data Capture
Datenbankänderungen als Ereignisse weitergeben
Die Idee und ihr Nutzen
Das Bestandssystem einer Versicherung ist ein altes Programm, das die Verträge verwaltet. Ein Sachbearbeiter ändert im Bestandssystem den Beitrag eines Vertrags, und das Bestandssystem speichert den neuen Beitrag in der Vertragsdatenbank. Das neue Kundenportal soll den neuen Beitrag ebenfalls anzeigen, doch das Bestandssystem kann keine Nachrichten an andere Programme schicken und soll nicht mehr umgebaut werden, weil kaum noch jemand den alten Code kennt. Die Vertragsdatenbank führt ohnehin ein Änderungsprotokoll, eine fortlaufende Liste, in die sie jede Änderung einträgt, also auch den neuen Beitrag. Deshalb liest ein zusätzliches Programm jede endgültig gespeicherte Änderung aus diesem Änderungsprotokoll und meldet die Änderung an das Kundenportal weiter. Dieses Verfahren heißt Change Data Capture, kurz CDC, auf Deutsch Erfassen von Datenänderungen.
Wie funktioniert es?
Ein Kunde mit dem Vertrag 88123 hat eine zusätzliche Deckung abgeschlossen. Der Sachbearbeiter ändert deshalb im Bestandssystem, einem Programm aus den Neunzigerjahren, den Jahresbeitrag von 42,10 Euro auf 45,80 Euro. Das Bestandssystem speichert den neuen Beitrag wie immer in einer Tabelle der Vertragsdatenbank. Die Vertragsdatenbank trägt die Änderung in ihr Änderungsprotokoll ein, eine fortlaufende Liste aller Änderungen, und bestätigt, dass die Änderung endgültig gespeichert ist. Der CDC-Leser, das zusätzliche Programm, liest dieses Änderungsprotokoll fortlaufend ab seiner letzten Leseposition, also ab dem Eintrag, bis zu dem er zuletzt gelesen hat. So erhält er die Änderung für Vertrag 88123. Aus dieser Änderung macht der CDC-Leser ein Ereignis, also eine Nachricht, die beschreibt, was geschehen ist, hier „Vertrag 88123: Beitrag jetzt 45,80 Euro“. Das Ereignis veröffentlicht er im Ereigniskanal, einem Nachrichtensystem, das Ereignisse bereithält, bis andere Programme sie abholen. Danach speichert der CDC-Leser seine neue Leseposition, damit er nach einem Neustart ab dieser Leseposition weiterlesen kann und nichts überspringt. Der Indexdienst, ein weiteres Programm, holt das Ereignis aus dem Ereigniskanal und schreibt den neuen Beitrag in den Suchindex des Kundenportals, eine für schnelle Suchen aufbereitete Kopie der Vertragsdaten. Sucht der Kunde nun im Kundenportal seinen Vertrag, sieht er 45,80 Euro.
Das Bestandssystem ist an der Weitergabe der Änderung nicht beteiligt und muss für den CDC-Leser nicht geändert werden. Der CDC-Leser nutzt, dass viele Datenbanken jede Änderung zuerst in ihr Änderungsprotokoll schreiben, bevor sie ihre Tabellen ändern. Mit dem Änderungsprotokoll stellen die Datenbanken ihren Stand nach einem Absturz oder nach dem Einspielen einer Sicherung wieder her und übertragen Änderungen an Kopien auf anderen Servern. Beim Datenbankprodukt PostgreSQL heißt das Änderungsprotokoll Write-Ahead Log, bei MySQL Binlog. Zum Lesen des Änderungsprotokolls bietet die Vertragsdatenbank eine Schnittstelle an, also eine vom Datenbankprodukt vorgegebene Form von Aufrufen, über die ein anderes Programm das Änderungsprotokoll abfragen darf. Der CDC-Leser ruft diese Schnittstelle auf und erhält darüber nur Änderungen, die die Vertragsdatenbank bereits bestätigt hat, und nur für die Tabellen, die er beobachten soll. Ob ein Ereignis neben dem neuen auch den alten Wert enthält, also neben 45,80 Euro auch 42,10 Euro, hängt von der Datenbank und ihren Einstellungen ab. Die Vertragsdatenbank ist ein gängiges Datenbankprodukt, das der Datenbankbetrieb der Versicherung betreut und einstellt.
CDC sorgt nicht von allein dafür, dass jede Änderung genau einmal im Kundenportal ankommt. Der CDC-Leser kann nach dem Veröffentlichen des Ereignisses abstürzen, bevor er die neue Leseposition gespeichert hat. Nach dem Neustart liest der CDC-Leser dann dieselbe Änderung erneut und veröffentlicht das Ereignis ein zweites Mal. Der Indexdienst muss deshalb damit umgehen können, dass er dasselbe Ereignis zweimal erhält. Weil das Ereignis den neuen Beitrag nennt und nicht etwa „Beitrag um 3,70 Euro erhöhen“, ergibt auch ein zweites Schreiben denselben Eintrag im Suchindex. Wie schnell eine Änderung ankommt, legt CDC ebenfalls nicht fest. Zwischen dem Speichern im Bestandssystem und der Anzeige im Kundenportal vergehen typischerweise Sekunden. Staut sich der Ereigniskanal oder ist der Indexdienst gestört, dauert es länger, und das Kundenportal zeigt so lange noch 42,10 Euro.
Change Data Capture: neuer Beitrag vom Altsystem bis ins Portal
Wann ist es sinnvoll und wann nicht?
Change Data Capture lohnt sich
- Ein Altsystem wie das Bestandssystem soll an neue Programme angebunden werden. Ein Altsystem ist ein älteres Programm, das weiterläuft, aber kaum noch geändert werden soll. Das Bestandssystem so umzubauen, dass es bei jeder Änderung eine Nachricht verschickt, hieße, alten Code zu ändern, den kaum noch jemand kennt.
- Andere Programme führen eigene Kopien der Vertragsdaten und sollen eine Änderung innerhalb von Sekunden bis Minuten erhalten. Neben dem Suchindex des Kundenportals sind das etwa Zwischenspeicher und Auswertungsdatenbanken. Ohne CDC enthalten diese Kopien bis zur nächsten Abfrage oder nächtlichen Lieferung noch den alten Beitrag.
- Die Vertragsdatenbank soll nicht durch regelmäßiges Auslesen der ganzen Vertragstabelle belastet werden. Der CDC-Leser liest nur die Änderungen, etwa die geänderte Zeile von Vertrag 88123 statt aller Verträge.
- Auch Zwischenstände sollen ankommen. Ändert sich ein Beitrag kurz hintereinander zweimal, entstehen zwei Ereignisse. Ein regelmäßiger Vergleich der Vertragstabelle mit dem Suchindex sähe nur den letzten Stand.
- Zu jeder gespeicherten Änderung soll eine Meldung entstehen, und ohne gespeicherte Änderung keine. Schreibt ein Programm erst in die Datenbank und verschickt danach getrennt eine Nachricht, geht bei einem Absturz zwischen beiden Schritten die Nachricht verloren. Der CDC-Leser liest dagegen jede gespeicherte Änderung aus dem Änderungsprotokoll, auch nach einem eigenen Absturz. Weil er nur bestätigte Änderungen erhält, entsteht zu einer Änderung, die die Datenbank am Ende nicht gespeichert hat, auch kein Ereignis.
- Ein Programm schreibt seine Ereignisse nach dem Outbox Pattern zuerst in eine Tabelle seiner Datenbank, die Ausgangstabelle. Der CDC-Leser liest die Ausgangstabelle aus und gibt die Ereignisse weiter. Ohne CDC müsste ein Programm die Ausgangstabelle in festen Abständen abfragen, und jedes neue Ereignis wartet bis zur nächsten Abfrage.
Change Data Capture ist nicht das Richtige
- Statt des Bestandssystems ändert ein neuer Vertragsdienst die Verträge, den sein Team ohnehin weiterentwickelt, und das Kundenportal soll fachliche Vorgänge wie „Kunde hat Deckung erweitert“ erfahren. Ein CDC-Leser auf der Vertragstabelle liefert aber nur geänderte Tabellenzeilen, etwa „Vertragstabelle, Zeile 88123, Beitrag jetzt 45,80“. Der Indexdienst müsste daraus den Vorgang ableiten und bei jeder Änderung am Aufbau der Vertragstabelle angepasst werden. Stattdessen baut das Team des Vertragsdienstes das Outbox Pattern ein: Der Vertragsdienst schreibt in derselben Datenbanktransaktion wie die Änderung ein fachliches Ereignis in die Ausgangstabelle. Eine Datenbanktransaktion ist ein Speichervorgang, der nur ganz oder gar nicht bestätigt wird. Weitergegeben werden dann nur die Ereignisse aus der Ausgangstabelle, und der Indexdienst bleibt unverändert, wenn sich der Aufbau der Vertragstabelle ändert.
- Die Vertragsdatenbank bietet kein Änderungsprotokoll, das ein anderes Programm lesen darf, oder der Datenbankbetrieb kann die nötigen Einstellungen und Berechtigungen nicht vergeben. Dann kann kein CDC-Leser die Änderungen erhalten. Stattdessen fragt der Indexdienst die Vertragstabelle in festen Abständen nach Verträgen ab, die seit der letzten Abfrage geändert wurden. Dafür braucht die Vertragstabelle eine Spalte mit dem Zeitpunkt der letzten Änderung, die das Bestandssystem schon füllt oder die Vertragsdatenbank bei jeder Änderung selbst setzt. Zwischenstände zwischen zwei Abfragen gehen aber verloren.
- Das Kundenportal muss nur einmal am Tag aktuell sein. Dann braucht niemand die Weitergabe innerhalb von Sekunden, für die CDC-Leser und Ereigniskanal laufen. Stattdessen lädt das Team des Kundenportals mit einer nächtlichen Stapelverarbeitung (Batch Processing) alle Verträge neu in den Suchindex. CDC-Leser und Ereigniskanal, die beide betrieben und überwacht werden müssen, entfallen. Zwischenstände gehen dabei verloren.
Nachteile und typische Fehler
Die Programme, die die Ereignisse empfangen, hängen vom Aufbau der Tabellen ab. Ändert sich der Aufbau einer Tabelle, ändern sich auch die Ereignisse, und alle Empfänger müssen angepasst werden. Eine fachliche Änderung verteilt sich manchmal über mehrere Tabellen. Die zusätzliche Deckung steht zum Beispiel in einer Tabelle für Deckungen, der neue Beitrag in der Vertragstabelle. Der Empfänger muss die einzelnen Ereignisse dann wieder zu einem Vorgang zusammensetzen. Für CDC braucht die Vertragsdatenbank oft besondere Einstellungen und Berechtigungen, die mit dem Datenbankbetrieb abgestimmt werden müssen. Hinzu kommt der Betrieb von CDC-Leser und Ereigniskanal, die beide überwacht werden müssen. Für das Änderungsprotokoll muss festgelegt sein, wie lange die Vertragsdatenbank es aufbewahrt. Je länger diese Frist ist, desto mehr Speicherplatz braucht die Vertragsdatenbank.
Typische Fehler und woran man sie erkennt:
- Änderungsprotokoll schon gelöscht: Die Vertragsdatenbank löscht alte Teile des Änderungsprotokolls nach einer festen Frist, so wie MySQL alte Teile des Binlog. Der CDC-Leser ist länger gestört, als diese Frist dauert. Nach der Störung fehlen dem CDC-Leser Änderungen, und der ganze Datenbestand muss neu übertragen werden. Erkennbar ist das an einer Fehlermeldung des CDC-Lesers, dass seine gespeicherte Leseposition im Änderungsprotokoll nicht mehr vorhanden ist.
- Volle Festplatte: PostgreSQL bewahrt alte Teile des Write-Ahead Log auf, bis jeder angemeldete CDC-Leser sie gelesen hat. Liest ein angemeldeter CDC-Leser lange nicht, wächst das Write-Ahead Log weiter und füllt die Festplatte der Datenbank. Erkennbar ist das an stetig wachsendem Speicherverbrauch des Write-Ahead Log, obwohl die Zahl der Änderungen gleich bleibt.
- Doppelte Ereignisse nicht erkannt: Der CDC-Leser veröffentlicht nach einem Neustart ein Ereignis ein zweites Mal. Der Indexdienst verarbeitet das Ereignis so, als wäre es neu. Bei Ereignissen, die einen neuen Wert nennen, bleibt das ohne Folgen. Zählt der Indexdienst aber zum Beispiel die Beitragsänderungen mit, stimmt die Zahl nicht mehr. Erkennbar ist das an Abweichungen zwischen Suchindex und Vertragsdatenbank nach einem Neustart des CDC-Lesers.
- Unbemerkter Rückstand: Der Ereigniskanal staut sich oder der Indexdienst ist gestört, ohne dass jemand die Verzögerung misst. Das Kundenportal zeigt stundenlang 42,10 Euro statt 45,80 Euro. Erkennbar ist das an Kundenanfragen zu alten Beträgen und an einem wachsenden Abstand zwischen der Leseposition des CDC-Lesers und dem Ende des Änderungsprotokolls.
Weiterführend
Verwandte Ansätze
- Event Driven Architecture: Bauweise, in der Programme auf Ereignisse reagieren, wie der CDC-Leser sie erzeugt.
- Strangler Fig: Vorgehen, ein Altsystem Stück für Stück abzulösen. CDC hält dabei die Daten des neuen Systems aktuell, solange das Altsystem noch Änderungen annimmt.
- Materialized View und CQRS: beschreiben eigene, für das Lesen aufbereitete Datenbestände, die sich aus solchen Änderungsereignissen aufbauen lassen.
Werkzeuge und Umsetzungen
- Debezium: liest die Änderungsprotokolle vieler Datenbanken, darunter PostgreSQL, MySQL und Oracle, und veröffentlicht die Änderungen als Ereignisse.
- Apache Kafka Connect: Rahmen, um Datenbanken an die Nachrichtenplattform Apache Kafka anzubinden. Leseprogramme wie Debezium laufen darin und speichern dort ihre Leseposition.
- Apache Flink CDC: liest Datenbankänderungen direkt in Verarbeitungsketten für fortlaufende Datenströme ein.
- Maxwell: liest das Änderungsprotokoll von MySQL und gibt die Änderungen im Textformat JSON aus.
Quellen
- Debezium: PostgreSQL Connector: beschreibt, wie der CDC-Leser das Protokoll von PostgreSQL liest und nach einem Neustart an der gespeicherten Position fortsetzt.
- Debezium: MySQL Connector: beschreibt unter anderem, wann Ereignisse doppelt geliefert werden können und was geschieht, wenn benötigte Teile des Binlogs bereits gelöscht sind.