CDC und logische Replikation für Datenbanken, die auf einem Open-Source-Stack implementiert sind

Hey Habr! SberTech ist in Kontakt – wir erstellen Platform V, die digitale Plattform von Sber für die Entwicklung von Geschäftsanwendungen.

Die Plattform umfasst mehr als 60 Produkte, die auf eigenen Open-Source-Builds basieren und in Bezug auf Funktionalität, Sicherheit, Leistung und Fehlertoleranz auf Unternehmensebene verbessert wurden.

In diesem Artikel sprechen wir über die Implementierung des Change Data Capture-Musters und die Datenreplikation zwischen Clustern im DataGrid-Produkt von Platform V, einer verteilten In-Memory-Datenbank für High-Performance-Computing. Und auch über die Features der Implementierung der Funktion und Replikationsmöglichkeiten. Unser Kollege Nikolai Izhikov vom Team für die Entwicklung von Datenbanken auf dem Open-Source-Stack hat beim Schreiben des Materials geholfen.

Was ist Änderungsdatenerfassung

Stellen Sie sich vor, Sie haben eine Datenbank mit geschäftskritischen Daten und strengen Lese-/Schreib-SLAs.

Gleichzeitig gibt es Systemkomponenten, die auf Datenänderungen reagieren müssen, wie z. B. Benachrichtigungen über eine neue Bestellung, eine Änderung der Benutzerdaten usw. Solche Komponenten gibt es viele, und ihre Anzahl wächst mit der Zeit.

Wenn die Datenbank bereits geladen ist, ist es irrational, die synchrone Verarbeitung an den Microservice anzuhängen, der die Bestellung erstellt, oder eine gespeicherte Prozedur zu schreiben – wir werden die Zeiten für Antworten erhöhen, es besteht die Gefahr, dass das SLA verletzt wird.

Und hier kommt das Change Data Capture- oder CDC-Muster zur Hilfe.

Wenn Änderungen an der Datenbank vorgenommen werden, schreibt sie ein Änderungsprotokoll (UNDO-, REDO-Protokolle), um Schreibvorgänge zu beschleunigen und Vorgänge zu optimieren. Die Datenbank schreibt sequentiell das von Ihnen geänderte Delta hinein.

CDC ist eine Anwendung, die Änderungsprotokolle verarbeiten, Datenänderungsereignisse daraus extrahieren und einen Änderungskonsumenten, der die Geschäftslogik implementiert, darüber benachrichtigen kann.

Als Ergebnis erhalten wir lineare, zeitlich geordnete Ereignisse von Datenänderungen: was war, was ist, welche Operation, auf welcher Tabelle oder in welchem ​​Cache ausgeführt wurde. Die Verarbeitung ist asynchron, kein Trigger oder gespeicherte Prozedur. Die Daten werden festgeschrieben, die Anwendung, die sie sendet, funktioniert weiter, und der Verbraucher erhält nach einiger Zeit eine Benachrichtigung über die Änderung.

Wie und wann CDC zu verwenden ist

• Streaming von Änderungen an das Data Warehouse. Sie haben DWH. Normalerweise sollten keine Echtzeitdaten darin eingehen. Um Daten zu übertragen, können Sie Prozeduren schreiben, die das Delta stündlich oder täglich ermitteln und an den Speicher senden. Mit Hilfe von CDC können dieselben Daten mit weniger Verzögerung übertragen werden.

  • Ereignisnachbearbeitung. Im System ist ein Ereignis aufgetreten – der Benutzer hat sich registriert, eine Bestellung erstellt, eine neue Datei hochgeladen – und je nach Geschäftsprozess müssen Moderationen oder andere Aktionen für einen neuen Datensatz eingeleitet werden.

  • Analytik. Gemäß den Ereignissen, die bei der CDC eintreffen, ist es möglich, Analysen in einem Modus nahezu in Echtzeit zu lesen.

  • logische Replikation. Bei der CDC haben wir ALLE Änderungen, die in der Datenbank passieren, zur Hand. Um die Replikation zu implementieren, müssen Sie sie nur zuverlässig auf einem Replikat ausführen.

CDC in einer Open-Source-Datenbank

Entwurf

Bei jeder Verfeinerung eines komplexen Systems, zu dem offensichtlich ein verteiltes DBMS gehört, besteht immer die Gefahr, dass etwas kaputt geht. Der beste Ausweg besteht darin, ein neues Feature zu erstellen, ohne die vorhandenen überhaupt zu berühren.

Daher entschied das Team beim Entwerfen von CDC auf der Grundlage von Ignite, dass ignite-cdc als separater Java-Prozess fungieren sollte, der den Ignite-Knoten nicht beeinflusst.

Ignite im Persistenzmodus schreibt wie jedes klassische DBMS Änderungen in das WAL (Write-Ahead Log). WAL ist eine Binärdatei, die Änderungen, Deltas enthält, die wir regelmäßig in den Hauptspeicher (Seitenspeicher) schreiben.

Von Zeit zu Zeit wandert ein WAL-Segment ins Archiv. Ignite-cdc sieht, dass ein archiviertes WAL-Segment angekommen ist und verarbeitet es.

Verarbeitung – Benachrichtigung des Verbrauchers über Änderungen. Es gibt eine öffentliche API für den Verbraucher, aber Sie können Ihre eigene schreiben.

Es ist wichtig, dass dadurch kein Speicherplatz verschwendet wird: Das WAL-Archiv ist die vorhandene Funktionalität, die für die Notfallwiederherstellung benötigt wird. Ignite-cdc verarbeitet genau die gleichen Segmente, es erscheinen keine neuen Daten auf der Platte.

Der nächste wichtige Punkt ist die Möglichkeit, den Lesezustand zu speichern. Ignite-cdc ist ein separater Prozess, der abstürzen kann. Sie müssen die Möglichkeit implementieren, den Zustand des Verbrauchers jedes Mal zu korrigieren, wenn er entscheidet, dass die Daten verarbeitet wurden und gespeichert werden können. Wenn es abstürzt, wird die Verarbeitung ab dem letzten Commit fortgesetzt. Glücklicherweise ist dies ziemlich einfach zu verwalten: Alles, was Sie tun müssen, ist, einen Zeiger an der Stelle im Segment zu halten, an der der Lesevorgang beendet wurde.

Aus der Fähigkeit, den Status zu speichern, ergibt sich die Fähigkeit, eine ausfallsichere Anwendung zu erstellen. Bei Problemen stürzt Ignite-cdc ab. Es wird davon ausgegangen, dass es mithilfe von OS-Mechanismen ausgelöst wird.

Auf Knotenebene sieht das so aus:

Es gibt eine kleine Feinheit: Das WAL-Archiv ist nicht unendlich, Ignite legt so viele Segmente in das Archiv, wie in den Einstellungen angegeben. Bei Archivierung von n+1 Segmenten wird das älteste gelöscht.

Um Situationen zu vermeiden, in denen CDC langsamer wurde und ein bereits gelöschtes Segment nicht verarbeitete, ist das Archivsegment fest mit einem Ordner verknüpft, mit dem nur Ignite-cdc arbeitet.

Wenn wir die Daten aus dem Archiv löschen, verbleibt die Datei im CDC-Ordner und die Daten sind verfügbar.

Wenn Ignite-cdc ein Segment verarbeitet hat, kann es sofort gelöscht werden. Die Daten verschwinden von der Festplatte, wenn beide Hardlinks entfernt werden.

Die Anwendung benötigt Metriken. Die API befindet sich bereits in Ignite und muss wiederverwendet werden.

API und Einstellungen

Um CDC zu konfigurieren, müssen drei Parameter auf Knotenebene konfiguriert werden.

öffentliche Klasse DataStorageConfiguration { long walForceArchiveTimeout; Zeichenfolge cdcWalPath; } öffentliche Klasse DataRegionConfiguration implementiert Serializable { boolean cdcEnabled; }

Hier:

  • cdcWalPath – Pfad zu dem Ordner, in dem WAL-Segmente für CDC hinzugefügt werden;

  • cdcEnabled – aktiviert CDC für DataRegion;

  • walForceArchiveTimeout — Zeitüberschreitung für erzwungene Archivierung des Segments: Auch wenn das Segment nicht vollständig gefüllt ist, wird es nach Zeitüberschreitung archiviert und steht für CDC zur Verfügung.

Es gibt eine Feinheit bei walForceArchiveTimeout. Das WAL-Archiv ist schnell, da es sich um eine speicherabgebildete Datei handelt. Dies ermöglicht es uns, tatsächlich nicht auf die Festplatte, sondern in den Speicher zu schreiben, sodass das Betriebssystem die Datei löscht, oder wir können dies manuell tun, wenn das Segment voll ist.

Das Schreiben auf die Festplatte ist ein teurer Vorgang, in dem die Leistung des Knotens abnimmt, daher sollte einerseits so selten wie möglich geschrieben werden. Andererseits erfährt die CDC von Änderungen, nachdem das Segment archiviert wurde, daher sollte die Aufzeichnung so oft wie möglich erfolgen. Kontroverse 🙂

Sie können es lösen, indem Sie ein Timeout entsprechend den Anforderungen der Anwendung wählen.

Das Interessanteste ist jetzt der Verbraucher, ein Zuhörer, mit dem Sie Änderungen erkennen und verarbeiten können:

öffentliche Schnittstelle CdcConsumer { public void start(MetricRegistry mreg); public boolean onEvents(Iterator events); public void onTypes(Iterator-Typen); public void onMappings(Iterator-Zuordnungen); öffentlich void stop(); }

  • start, stop – für Initialisierung und Stopp;

  • onEvents – Rückruf für die Verarbeitung von Änderungen: true zurückgegeben – der Status wurde festgeschrieben;

  • onTypes, onMappings – Callbacks zur Behandlung von Änderungen an Metainformationen zu Typen.

Was ist in der Veranstaltung verfügbar:

öffentliche Schnittstelle CdcEvent erweitert Serializable { public Object key(); @Nullable öffentlicher Objektwert(); öffentliche boolesche primäre (); öffentliche int-Partition (); öffentliche CacheEntryVersion version(); public int cacheId(); }

  • Schlüssel, Wert – Daten: Wert kann null sein, wenn das Ereignis von remove ist;

  • primär – das Ereignis ist auf dem primären oder dem Backup aufgetreten;

  • partition – Partitionsnummer, die benötigt wird, um die Last gemäß den vorhandenen Partitionen in Ignite zu verteilen;

  • Version – Einstiegsversion;

  • cacheId – Cache-ID.

Somit haben wir eine Anwendung, die asynchron Benachrichtigungen über alle Änderungen an allen Daten innerhalb des Ignite-Clusters empfängt. Basierend auf dieser Funktionalität können wir nun sowohl die erforderlichen Geschäftsfunktionen als auch die logische Replikation implementieren.

Logische Replikation mit CDC

Mit physischer Replikation in diesem Artikel meine ich die Übertragung zwischen Datenbankinstanzen der internen Darstellung von Daten: Speicherseiten usw.

Unter der logischen Auswahl des Änderungsflusses aus der Quelldatenbank und seiner Reproduktion in der Zieldatenbank.

Mit CDC können Sie die logische Replikation implementieren.

Ignite unterstützt zwei Schemas: Ignite to Ignite und Ignite to Kafka.

Zünden, um zu zünden

Innerhalb von Ignite-cdc arbeitet IgniteToIgniteCdcStreamer, der übrigens standardmäßig verfügbar ist. Dies ist ein Konsument, der einen Ignite-Client-Knoten in sich selbst auslöst, sich mit dem Empfänger-Cluster verbindet und beim Empfang von Änderungen eine fast normale Put-Operation an den Empfänger-Cluster sendet.

Wenn der Quellcluster nicht verfügbar ist, z. B. aufgrund eines ausgefallenen Knotens, wartet Ignite-cdc ewig auf den Start des Knotens. Neue Daten werden nicht eintreffen, und der Prozess wird diejenigen verarbeiten, die von dem noch aktiven Knoten generiert wurden.

Wenn Ignite-cdc gefallen ist, wird es zunächst auf allen anderen Knoten am Leben sein. Zweitens wird es nach einer Weile vom Betriebssystem neu gestartet, CDC wird sich ansehen, welche Änderungen es verarbeitet hat, und sie weiterhin an das benachbarte Cluster senden.

Wenn ein benachbarter Cluster oder die Netzwerkverbindung verloren geht, fällt Ignite-cdc ebenfalls ab und kehrt nach einem Neustart zum Zielcluster zurück. Wenn der Cluster nicht verfügbar ist – ein Sturz. Wenn verfügbar, großartig, CDC beginnt mit dem Senden von Änderungen, die sich in WAL auf der Festplatte angesammelt haben. Die Festplatte ist ein Puffer von Änderungen, die akkumuliert werden, bis sie verarbeitet und an den gewünschten Punkt gesendet werden können.

Entzünde dich an Kafka

Dies ist eine Replikationsoption für Situationen, in denen sich Ignite-Cluster nicht sehen können, Kafka als Transport verwenden müssen oder wenn mehrere Ereignisleser vorhanden sind.

Das Schema ist fast das gleiche: Der Streamer IgniteToKafkaCdcStreamer wird verwendet, um Ereignisse zu verarbeiten. Es zerlegt Daten gemäß Ignite-Partitionen in Kafka-Partitionen.

Auf der Empfängerseite gibt es eine Kafka-to-Ignite-Anwendung – sie liest Daten von Kafka und legt sie in den empfangenden Ignite-Cluster.

Konfliktlöser

Jetzt kommen wir zum interessantesten Teil: Was passiert, wenn ein Schlüssel auf beiden Clustern geändert wird?

Antwort – Konfliktlöser wird funktionieren. Dies ist eine Schnittstelle, die genau bestimmt, welche Daten in den Cluster gelangen sollen. Es kann den “alten”, “neuen” Wert annehmen oder eine Zusammenführung durchführen.

Die CDC-Erweiterung bietet eine Standardimplementierung, aber Sie können Ihre eigene implementieren. Gleichzeitig ist zu beachten, dass es keine richtige Lösung für Änderungskonflikte gibt. Ohne etwas über die Daten zu wissen, ist es unmöglich, genau festzustellen, welche Änderung richtig ist und welche nicht.

Schlüsseleigenschaften der Standardimplementierung:

  1. Wenn die Änderung auf dem “lokalen” Cluster erfolgt ist, gewinnt es.

  2. Änderungen aus demselben Cluster werden nach Version verglichen. Eine Änderung mit einer größeren Version gewinnt.

  3. Wenn ein Vergleichsfeld angegeben ist, werden Datensätze anhand dieses Felds verglichen.

  4. Wenn alle vorherigen fehlschlagen, wird der neue Eintrag verworfen. Die Daten bewegen sich in den Warnprotokollen, und Sie müssen darüber nachdenken, was als nächstes zu tun ist.

Fazit

Die Einführung des CDC-Musters ermöglichte es, die erforderliche Funktionalität zum Implementieren von Ereignisabonnements und Erstellen von Replikaten hinzuzufügen, ohne die Leistung der Datenbank-Engine selbst zu beeinträchtigen.

Similar Posts

Leave a Reply

Your email address will not be published.