Nebenbei benötigt: Apache Ignite Cluster
Ein Cluster in Entzünden ist ein Satz von Server und Client - Knoten, in dem Serverknoten in eine logische Struktur in Form eines Rings kombiniert werden, und Client - Knoten sind zu dem entsprechenden Serverknoten verbunden ist . Der Hauptunterschied zwischen Clientknoten und Serverknoten besteht darin, dass erstere keine Daten speichern.
Aus logischer Sicht gehören Daten zu Partitionen, die gemäß einer bestimmten Affinitätsfunktion über Knoten verteilt sind ( mehr zur Datenverteilung in Ignite ). In den Hauptpartitionen ( primären Partitionen) können sich Kopien ( Backups ) befinden.
Funktionsweise von Transaktionen in Apache Ignite
Die Clusterarchitektur in Apache Ignite stellt eine bestimmte Anforderung an den Transaktionsmechanismus: Datenkonsistenz in einer verteilten Umgebung. Dies bedeutet, dass Daten, die sich auf verschiedenen Knoten befinden, ganzheitlich im Sinne der ACID- Prinzipien geändert werden müssen . Es gibt eine Reihe von Protokollen, mit denen Sie das tun können, was Sie wollen. Apache Ignite verwendet einen zweiphasigen Festschreibungsalgorithmus , der aus zwei Stufen besteht:
- bereiten;
- verpflichten;
Beachten Sie, dass sich die Details in den Phasen abhängig von der Isolationsstufe der Transaktion , dem Mechanismus zum Aufheben von Sperren und einer Reihe anderer Parameter ändern können.
Lassen Sie uns anhand der folgenden Transaktion sehen, wie beide Phasen ablaufen:
Transaction tx = client.transactions().txStart(PESSIMISTIC, READ_COMMITTED);
client.cache(DEFAULT_CACHE_NAME).put(1, 1);
tx.commit();
Phase vorbereiten
- — (near node Apache Ignite) — prepare- , primary- , .
- primary- Prepare- backup-, , . backup- .
- backup- Acknowledge- primary-, , , , .
Commit
Nach dem Empfang von Bestätigungsnachrichten von allen Knoten, die primäre Partitionen enthalten, sendet der Transaktionskoordinatorknoten eine Commit-Nachricht, wie in der folgenden Abbildung gezeigt.
Eine Transaktion gilt als abgeschlossen, sobald der Transaktionskoordinator alle Bestätigungsnachrichten erhalten hat.
Von der Theorie zur Praxis
Um die Logik einer Transaktion zu betrachten, wenden wir uns der Ablaufverfolgung zu.
Gehen Sie folgendermaßen vor, um die Ablaufverfolgung in Apache Ignite zu aktivieren:
- Lassen Sie uns das Modul ignite -opencensus aktivieren und OpenCensusTracingSpi über die Clusterkonfiguration als tracingSpi festlegen:
<bean class="org.apache.ignite.configuration.IgniteConfiguration"> <property name="tracingSpi"> <bean class="org.apache.ignite.spi.tracing.opencensus.OpenCensusTracingSpi"/> </property> </bean>
oder
IgniteConfiguration cfg = new IgniteConfiguration(); cfg.setTracingSpi( new org.apache.ignite.spi.tracing.opencensus.OpenCensusTracingSpi());
- Lassen Sie uns einige Stichproben-Transaktionen ungleich Null festlegen:
JVM_OPTS="-DIGNITE_ENABLE_EXPERIMENTAL_COMMAND=true" ./control.sh --tracing-configuration set --scope TX --sampling-rate 1
oder
ignite.tracingConfiguration().set( new TracingConfigurationCoordinates.Builder(Scope.TX).build(), new TracingConfigurationParameters.Builder(). withSamplingRate(SAMPLING_RATE_ALWAYS).build());
:
- API
JVM_OPTS="-DIGNITE_ENABLE_EXPERIMENTAL_COMMAND=true" - sampling-rate , , . , .
- , SPI, . , , .
- API
- PESSIMISTIC, SERIALIZABLE .
Transaction tx = client.transactions().txStart(PESSIMISTIC, SERIALIZABLE); client.cache(DEFAULT_CACHE_NAME).put(1, 1); tx.commit();
Wenden wir uns an das GridGain Control Center (eine detaillierte Übersicht über das Tool) und sehen uns den resultierenden Bereichsbaum an: In der Abbildung sehen wir, dass der zu Beginn der Transaktionen erstellte Transaktionsstammbereich (). TxStart-Aufruf zwei bedingte Bereichsgruppen generiert:
- Die durch die put () -Operation ausgelöste Lock-Grabbing-Maschine:
- transactions.near.enlist.write
- transactions.colocated.lock.map
- transactions.commit, tx.commit(), , , — prepare finish Apache Ignite (finish- commit- ).
Schauen wir uns nun die Vorbereitungsphase einer Transaktion genauer an, die ab dem Transaktionskoordinatorknoten (in Apache Ignite-Begriffen nahe am Knoten) die Spanne transaction.near.prepare erzeugt.
Auf der primären Partition löst die Vorbereitungsanforderung die Erstellung der Transactions.dht.prepare-Spanne aus, innerhalb derer Vorbereitungsanforderungen an die Sicherungen von tx.process.prepare.req gesendet werden, wo sie von tx.dht.process.prepare.response verarbeitet und gesendet werden zurück zur primären Partition, die eine Bestätigungsnachricht an den Transaktionskoordinator sendet, während eine Spanne tx.near.process.prepare.response erstellt wird. Die Fertigstellungsphase in diesem Beispiel ähnelt der Vorbereitungsphase, sodass wir keine detaillierten Analysen benötigen.
Wenn Sie auf einen der Bereiche klicken, werden die entsprechenden Metainformationen angezeigt:
So sehen wir beispielsweise für die Stammtransaktionsspanne, dass sie auf dem Clientknoten 0eefd erstellt wurde.
Wir können auch die Granularität der Transaktionsverfolgung erhöhen, indem wir die Verfolgung des Kommunikationsprotokolls aktivieren.
Ablaufverfolgungsparameter einrichten
JVM_OPTS="-DIGNITE_ENABLE_EXPERIMENTAL_COMMAND=true" ./control.sh --tracing-configuration set --scope TX --included-scopes Communication --sampling-rate 1 --included-scopes COMMUNICATION
ignite.tracingConfiguration().set(
new TracingConfigurationCoordinates.Builder(Scope.TX).build(),
new TracingConfigurationParameters.Builder().
withIncludedScopes(Collections.singleton(Scope.COMMUNICATION)).
withSamplingRate(SAMPLING_RATE_ALWAYS).build())
Jetzt haben wir Zugriff auf Informationen über die Übertragung von Nachrichten über das Netzwerk zwischen Clusterknoten, die beispielsweise dazu beitragen, die Frage zu beantworten, ob ein potenzielles Problem durch Nuancen der Netzwerkkommunikation verursacht wurde. Wir werden uns nicht mit den Details befassen, sondern nur feststellen, dass die Bereiche socket.write und socket.read für das Schreiben in den Socket und das Lesen dieser bzw. jener Nachricht verantwortlich sind.
Ausnahmebehandlung und Crash-Wiederherstellung
Wir sehen also, dass die Implementierung des verteilten Transaktionsprotokolls in Apache Ignite dem kanonischen nahe kommt und es Ihnen ermöglicht, abhängig von der ausgewählten Transaktionsisolationsstufe den richtigen Grad an Datenkonsistenz zu erzielen. Offensichtlich steckt der Teufel im Detail und eine große Logikschicht blieb außerhalb des Rahmens des oben analysierten Materials. So haben wir beispielsweise die Mechanismen des Betriebs und der Wiederherstellung von Transaktionen im Falle eines Sturzes der daran beteiligten Knoten nicht berücksichtigt. Wir werden das jetzt beheben.
Wir haben oben gesagt, dass im Zusammenhang mit Transaktionen in Apache Ignite drei Arten von Knoten unterschieden werden können:
- Transaktionskoordinator (in der Nähe des Knotens);
- Primärknoten für den entsprechenden Schlüssel (Primärknoten);
- Knoten mit Sicherungsschlüsselpartitionen (Sicherungsknoten);
und zwei Phasen der Transaktion selbst:
- Bereiten;
- Fertig;
Durch einfache Berechnungen erhalten wir die Notwendigkeit, sechs Optionen für Knotenabstürze zu verarbeiten - von einem Backup-Sturz während der Vorbereitungsphase bis zu einem Sturz des Transaktionskoordinators während der Endphase. Lassen Sie uns diese Optionen genauer betrachten.
Fallende Sicherung sowohl in der Vorbereitungs- als auch in der Endphase
Diese Situation erfordert keine zusätzlichen Maßnahmen. Die Daten werden im Rahmen des Neuausgleichs vom Primärknoten unabhängig voneinander an die neuen Sicherungsknoten übertragen.
Fallender Primärknoten in der Vorbereitungsphase
Wenn das Risiko besteht, inkonsistente Daten zu erhalten, löst der Transaktionskoordinator eine Ausnahme aus. Dies ist ein Signal für die Übertragung der Kontrolle, um eine Entscheidung zum Neustart der Transaktion oder eine andere Möglichkeit zur Lösung des Problems für die Clientanwendung zu treffen.
Sturz des Primärknotens in der Endphase
In diesem Fall wartet der Transaktionskoordinator nach dem Empfang auf zusätzliche NodeFailureDetection-Nachrichten, über die er über den erfolgreichen Abschluss der Transaktion entscheiden kann, ob die Daten auf die Sicherungspartitionen geschrieben wurden.
Sturz des Transaktionskoordinators
Der interessanteste Fall ist der Verlust des Transaktionskontexts. In einer solchen Situation tauschen der primäre und der Sicherungsknoten den lokalen Transaktionskontext direkt miteinander aus, wodurch der globale Kontext wiederhergestellt wird, wodurch eine Entscheidung zur Überprüfung des Commits getroffen werden kann. Wenn beispielsweise einer der Knoten meldet, dass keine Abschlussnachricht empfangen wurde, wird die Transaktion zurückgesetzt.
Zusammenfassung
In den obigen Beispielen haben wir den Transaktionsfluss untersucht und ihn mithilfe der Ablaufverfolgung veranschaulicht, die die interne Logik im Detail zeigt. Wie Sie sehen können, ähnelt die Implementierung von Transaktionen in Apache Ignite dem klassischen Konzept des zweiphasigen Festschreibens mit einigen Verbesserungen im Bereich der Transaktionsleistung in Bezug auf den Mechanismus zum Aufheben von Sperren, die Merkmale der Wiederherstellung nach Fehlern und die Logik des Transaktionszeitlimits.