Verteilte Softwarearchitektur – Transaktion ACID

Geschäftskonzept

Die Transaktionsverarbeitung ist ein Problem, das in fast jedem Informationssystem auftritt. Der Sinn seiner Existenz besteht darin, sicherzustellen, dass die Daten im System korrekt sind und es keine Widersprüche zwischen verschiedenen Daten gibt, dh die Konsistenz der Daten sicherzustellen Datenstatus (Konsistenz)

In Bezug auf die Konsistenz konzentrieren wir uns auf die Konsistenz des Datenbankstatus. Bei der Verteilung ist die Konsistenz, die im diskutierten verteilten Konsensalgorithmus erwähnt wird, unterschiedlich. Wenn man von der Konsistenz des Datenbankstatus spricht, erfordert das Erreichen dieses Ziels theoretisch die gemeinsame Anstrengung von drei Aspekten:

  • Atomar: Im selben Geschäftsprozess garantiert eine Transaktion, dass mehrere Datenänderungen entweder gleichzeitig erfolgreich sind oder gemeinsam widerrufen werden.

  • Isolation: In verschiedenen Geschäftsprozessen stellen Transaktionen sicher, dass die von jedem Unternehmen gelesenen und geschriebenen Daten unabhängig voneinander sind und sich nicht gegenseitig beeinflussen.

  • Haltbarkeit: Die Transaktion sollte sicherstellen, dass alle erfolgreich übermittelten Datenänderungen korrekt und ohne Datenverlust beibehalten werden können.

  • A, I, D sind die Mittel und C ist das Ziel.

Geschäftsszene

Transaktionsszenarien Das Konzept von Transaktionen stammt ursprünglich aus Datenbanken, aber in heutigen Informationssystemen sind alle Szenarien, die Datenkorrektheit (Konsistenz) erfordern, einschließlich, aber nicht beschränkt auf, Datenbanken, Caches, Transaktionsspeicher, Nachrichten, Warteschlangen und Objektdateispeicher usw. kann eine Transaktionsverarbeitung beinhalten.

Wenn ein Dienst nur eine Datenquelle betreibt, ist es relativ einfach, Konsistenz über A, I und D zu erreichen, wenn ein Dienst jedoch mehrere unterschiedliche Datenquellen umfasst oder sogar mehrere unterschiedliche Dienste gleichzeitig mehrere unterschiedliche Datenquellen umfassen. Wann Da es keine Datenquelle gibt, wird diese Angelegenheit sehr schwierig und erfordert manchmal einen hohen oder sogar unrealistischen Preis. Daher hat die Branche viele andere Lösungen untersucht, um die höchstmögliche Konsistenz zu erreichen und gleichzeitig die Bedienbarkeit sicherzustellen.

Infolgedessen hat sich die Transaktionsverarbeitung von einem „Programmierproblem“ in bestimmten Vorgängen zu einem „Architekturproblem“ entwickelt, das sorgfältig abgewogen werden muss. Bei der Erforschung dieser Geschäftspläne haben die Menschen viele neue Ideen und Konzepte hervorgebracht. Lassen Sie uns die unterschiedliche Verarbeitung desselben Falles in verschiedenen Geschäftsplänen untersuchen, um diese Konzepte durchzugehen und zu begradigen.

Szenario

Stellen Sie zunächst den konkreten Fall vor.
Fenix's Bookstore ist ein Online-Buchladen. Damit ein Produkt erfolgreich verkauft werden kann, muss sichergestellt werden, dass die folgenden drei Dinge richtig gehandhabt werden:

  1. Das Konto des Benutzers wird von der entsprechenden Warenzahlung abgezogen;

  2. Ziehen Sie den Bestand im Warenlager ab und markieren Sie die Ware als auf Lieferung wartend.

  3. Das Konto des Händlers erhöht die entsprechende Produktzahlung.

Als nächstes werde ich die verschiedenen Szenarien vorstellen: „Ein einzelner Dienst verwendet eine einzelne Datenquelle“, „Ein einzelner Dienst verwendet mehrere Datenquellen“, „Mehrere Dienste verwenden eine einzelne Datenquelle“ und „Mehrere Dienste verwenden mehrere Datenquellen“. Was bedeutet das? Wir verwenden, um die Richtigkeit der oben genannten Szenariobeispiele sicherzustellen. Schauen wir uns in der heutigen Vorlesung zunächst „Ein einzelner Dienst nutzt eine einzelne Datenquelle“ an, also das lokale Transaktionsszenario.

Lokale Transaktionen

Eine lokale Transaktion bezieht sich auf eine Transaktion, die nur eine bestimmte einzelne Transaktionsressource betreibt und nicht die Koordination des „globalen Transaktionsmanagers“ erfordert.
Lokale Transaktionen sind das grundlegendste Transaktionsverarbeitungsschema, das normalerweise nur auf Szenarien anwendbar ist, in denen ein einzelner Dienst eine einzelne Datenquelle verwendet, und deren Funktionsfähigkeit direkt von der Transaktionsfähigkeit der Datenquelle (normalerweise des Datenbanksystems) selbst abhängt.

Auf der Ebene des Programmcodes können wir höchstens eine Schicht standardisierter Verpackung (z. B. eine JDBC-Schnittstelle) auf der Transaktionsschnittstelle erstellen und nicht tief in den Betriebsprozess der Transaktion eingreifen.

Lassen Sie mich Ihnen ein konkretes Beispiel geben. Angenommen, Ihr Code ruft die Methode Transaction::rollback() in JDBC auf. Die erfolgreiche Ausführung der Methode bedeutet nicht, dass die Transaktion erfolgreich zurückgesetzt wurde. Wenn die Engine der Datentabelle MyISAM ist , dann ist die rollback()-Methode ein bedeutungsloses No-Op. Wenn wir lokale Transaktionen ausführlich besprechen wollen, müssen wir daher über die Ebene des Anwendungscodes hinausgehen, um einige Prinzipien der Transaktionsimplementierung der Datenbank selbst zu verstehen und herauszufinden, wie herkömmliche Datenbankverwaltungssysteme ACID implementieren.

Die ARIES-Theorie (Algorithms for Recovery and Isolation Exploiting Semantics) ist ein semantikbasierter Wiederherstellungs- und Isolationsalgorithmus, der sich auf die Lösung der drei ACID-Attribute von Transaktionen, Atomizität (Atomic), Isolation (Isolation) und Haltbarkeit (Durability) konzentriert auf algorithmischer Ebene umgesetzt werden.

Atomarität und Persistenz erreichen

Atomarität und Persistenz erreichen Atomarität und Persistenz sind zwei eng miteinander verbundene Attribute in einer Transaktion. Atomarität stellt sicher, dass mehrere Vorgänge einer Transaktion entweder wirksam werden oder nicht wirksam werden, und es wird keinen Zwischenzustand geben; Persistenz garantiert, dass die Transaktion einmal wirksam wird Der geänderte Inhalt wird aus keinem Grund widerrufen oder geht verloren.

Es ist offensichtlich, dass Daten erfolgreich in persistente Speicher wie Festplatten und Bänder geschrieben werden müssen, bevor sie dauerhaft gespeichert werden können. Daten werden nur im Speicher gespeichert, sobald das Programm plötzlich abstürzt, die Datenbank abstürzt, das Betriebssystem abstürzt und die Maschine plötzlich die Stromversorgung verliert und Abstürze (Später werden wir es zusammenfassend als Absturz, Absturz bezeichnen) und andere Situationen gehen verloren. Die Schwierigkeit beim Erreichen von Atomizität und Beständigkeit besteht darin, dass der Vorgang des „Schreibens auf die Festplatte“ nicht atomar ist . Es gibt nicht nur „Schreiben“ und „Nicht-Schreiben“, sondern auch einen objektiven Zwischenzustand des „Schreibens“.

Gemäß dem oben aufgeführten Beispielszenario sind für den Kauf eines Buchs im Fenix's Bookstore drei Datenänderungen erforderlich:

  1. Ziehen Sie die Zahlung vom Benutzerkonto ab,

  2. Zahlung zum Händlerkonto hinzufügen,

  3. Markieren Sie ein Buch im Warenlager als versandt.

Aufgrund des Zwischenzustands des Schreibvorgangs können folgende Szenarien auftreten:

Nicht festgeschriebene Transaktion : Das Programm hat die Änderung der drei Daten noch nicht abgeschlossen und die Datenbank hat die Änderungen von einem oder zwei der Daten auf die Festplatte geschrieben. Zu diesem Zeitpunkt kommt es zu einem Absturz. Nach dem Neustart muss die Datenbank eine Möglichkeit haben, dies herauszufinden dass es vor dem Absturz einmal passiert ist. Durch unvollständige Einkaufsvorgänge werden die geänderten Daten von der Festplatte in den unveränderten Zustand zurückversetzt, um die Atomizität sicherzustellen.

Übermittelte Transaktion : Das Programm hat drei Daten geändert und die Datenbank hat noch nicht alle drei Datenänderungen auf die Festplatte geschrieben. Zu diesem Zeitpunkt kommt es zu einem Absturz. Nach dem Neustart muss die Datenbank wissen können, dass zuvor eine vollständige Transaktion stattgefunden hat Der Absturz. Der Einkaufsvorgang schreibt den Teil der Daten neu, der keine Zeit hatte, auf die Festplatte geschrieben zu werden, um die Persistenz sicherzustellen. Dieser Datenwiederherstellungsvorgang wird als Absturzwiederherstellung (Crash Recovery, auch bekannt als Failure Recovery oder Transaction Recovery) bezeichnet.

Um die Wiederherstellung nach einem Absturz erfolgreich abzuschließen, kann das Schreiben von Daten auf die Festplatte einen bestimmten Wert einer bestimmten Zeile und Spalte einer Tabelle nicht direkt ändern, wie es bei einem Programm der Fall ist, das einen Variablenwert im Speicher ändert. Alle für den Vorgang der Datenänderung erforderlichen Informationen ( B. welche Daten geändert werden, auf welcher Speicherseite und in welchem ​​Festplattenblock sich die Daten physisch befinden, welcher Wert in welchen Wert geändert wird usw.) in Form eines Protokolls (das Protokoll bezieht sich auf die Dateischreibmethode, die nur ausgeführt wird). sequentielles Anhängen, was der effizienteste Schreibmodus ist) zuerst auf die Festplatte.

Erst wenn alle Protokolldatensätze sicher auf der Festplatte abgelegt sind und der „Commit-Datensatz“, der die erfolgreiche Übermittlung der Transaktion darstellt, sichtbar ist, ändert die Datenbank die tatsächlichen Daten entsprechend den Informationen im Protokoll. Nach Abschluss der Änderung wird ein „End Record“ wird dem Protokoll hinzugefügt. „Zeigt an, dass die Transaktion beibehalten wurde. Diese Transaktionsimplementierungsmethode wird „Commit Logging“ genannt.

Zusätzliches Wissen: Shadow Paging
erkennt, dass die Atomizität und Dauerhaftigkeit von Transaktionen durch Protokolle heute eine gängige Lösung ist, aber nicht die einzige Option. Zusätzlich zu den Protokollen gibt es einen weiteren Transaktionsimplementierungsmechanismus namens „ Shadow Paging “ (in chinesischen Materialien als „Shadow Paging“ übersetzt). Der von der häufig verwendeten leichten Datenbank SQLite Version 3 verwendete Transaktionsmechanismus ist Shadow Paging.
Die allgemeine Idee von Shadow Paging besteht darin, dass Änderungen an Daten auf die Daten auf der Festplatte geschrieben werden. Anstatt jedoch die Originaldaten direkt an Ort und Stelle zu ändern, wird zunächst eine Kopie der Daten, die Originaldaten, kopiert bleibt erhalten und die Kopierdaten werden geändert. Während des Transaktionsprozesses liegen die geänderten Daten in zwei Kopien gleichzeitig vor, eine davon sind die Daten vor der Änderung und die andere sind die Daten nach der Änderung, was auch den Ursprung des Namens „Shadow“ darstellt.
Wenn die Transaktion erfolgreich übermittelt wurde und alle Datenänderungen erfolgreich beibehalten wurden, besteht der letzte Schritt darin, den Referenzzeiger der Daten zu ändern, die Referenz von den Originaldaten auf die neu kopierte geänderte Kopie zu ändern und die letzte Operation „Zeiger ändern“ auszuführen Es handelt sich um einen atomaren Vorgang, und der Schreibvorgang moderner Festplatten kann als Hardware-Garantie dafür angesehen werden, dass das Phänomen der „Halbwertsänderung“ nicht auftritt. Daher kann Shadow Paging auch Atomizität und Persistenz gewährleisten.
Mit Shadow Paging lassen sich Transaktionen einfacher implementieren als mit Commit Logging. Wenn es jedoch um Isolation und Parallelitätssperren geht, ist die Fähigkeit zur Transaktionsgleichzeitigkeit von Shadow Paging relativ begrenzt, sodass es in Hochleistungsdatenbanken nicht weit verbreitet ist.

Das Prinzip der Commit-Protokollierung zur Gewährleistung der Datenpersistenz und Atomizität:
Erstens ist die gesamte Transaktion erfolgreich, sobald das Protokoll erfolgreich in den Commit-Datensatz geschrieben wurde. Auch wenn sie beim Ändern der Daten abstürzt, stellen Sie die Site nach dem Neustart gemäß dem Protokoll wieder her Informationen, die auf die Festplatte geschrieben wurden, und fahren Sie fort. Ändern Sie einfach die Daten, um die Persistenz sicherzustellen. Zweitens: Wenn das Protokoll abstürzt, ohne dass es erfolgreich geschrieben wurde, sehen Sie nach dem Neustart des Systems einige Protokolle ohne Commit-Datensatz. Markieren Sie diesen Teil des Protokolls dann als Rollback-Status, und die gesamte Transaktion wird so sein, als ob sie nie stattgefunden hätte , was die Atomizität garantiert.

Commit Logging implementiert Transaktionen einfach und klar, und einige Datenbanken verwenden den Commit Logging-Mechanismus, um Transaktionen zu implementieren (repräsentativer: Alis OceanBase). Es gibt jedoch einen großen Fehler bei der Commit-Protokollierung: Alle tatsächlichen Änderungen an den Daten müssen nach dem Commit-Datensatz der Transaktion erfolgen und das Protokoll wird in den Commit-Datensatz geschrieben, selbst wenn die Festplatten-E/A vor dem Commit der Transaktion ausreichend frei ist, selbst wenn die Durch eine Transaktion geänderte Daten Das Datenvolumen ist sehr groß und beansprucht viel Speicherpufferung. Unabhängig vom Grund dürfen die Daten auf der Festplatte niemals geändert werden, bevor die Transaktion festgeschrieben wird. Dies wirkt sich sehr nachteilig auf die Verbesserung aus die Leistung der Datenbank.

Um diesen Mangel zu beheben, kann endlich die ARIES-Theorie auftauchen. ARIES schlug eine Protokollverbesserungslösung für die „Write-Ahead-Protokollierung“ vor. Das sogenannte „Write-Ahead“ im Namen bedeutet, dass geänderte Daten im Voraus geschrieben werden können, bevor die Transaktion festgeschrieben wird.

Bei der Write-Ahead-Protokollierung wird der Zeitpunkt, zu dem geänderte Daten geschrieben werden sollen, zunächst in zwei Typen unterteilt: FORCE und STEAL entsprechend dem Transaktionszeitpunkt:

  • FORCE : Nachdem die Transaktion festgeschrieben wurde, wird sie FORCE genannt, wenn die geänderten Daten gleichzeitig geschrieben werden müssen, und sie wird NO-FORCE genannt, wenn die geänderten Daten gleichzeitig geschrieben werden müssen. In Wirklichkeit verwenden die meisten Datenbanken die NO-FORCE-Strategie. Solange Protokolle vorhanden sind, können die geänderten Daten jederzeit beibehalten werden. Aus Sicht der Optimierung der Festplatten-E/A-Leistung besteht keine Notwendigkeit, das Schreiben von Daten zu erzwingen sofort.
  • STEAL : Bevor die Transaktion festgeschrieben wird, wird sie STEAL genannt, wenn die geänderten Daten im Voraus geschrieben werden dürfen, und sie wird NO-STEAL genannt, wenn dies nicht erlaubt ist. Im Hinblick auf die Optimierung der Festplatten-E/A-Leistung ist die Möglichkeit, Daten im Voraus zu schreiben, förderlich für die Nutzung ungenutzter E/A-Ressourcen und die Einsparung von Speicher im Datenbank-Cache.

Die Commit-Protokollierung erlaubt NO-FORCE, aber kein STEAL. Denn wenn ein Teil der geänderten Daten vor dem Festschreiben der Transaktion auf die Festplatte geschrieben wird, werden die im Voraus geschriebenen geänderten Daten zu einem Fehler, sobald die Transaktion zurückgesetzt wird oder ein Absturz auftritt.

Die Write-Ahead-Protokollierung ermöglicht NO-FORCE und auch STEAL. Die Lösung besteht darin, ein weiteres Protokoll namens Undo Log hinzuzufügen. Bevor die geänderten Daten auf die Festplatte geschrieben werden, muss zunächst das Rückgängig-Protokoll aufgezeichnet werden, in dem angegeben wird, an welcher Position die Daten geändert werden und von welchem ​​Wert auf welchen Wert, damit die Daten geschrieben werden, wenn die Transaktion zurückgesetzt oder der Absturz behoben wird Im Voraus kann das Rückgängig-Protokoll geändert werden, um es zu löschen.

Undo Log wird jetzt allgemein als „Rollback-Protokoll“ übersetzt. Das zuvor aufgezeichnete Protokoll zur Wiedergabe von Datenänderungen während der Wiederherstellung nach einem Absturz wird entsprechend Redo Log genannt, was allgemein als „Redo-Protokoll“ übersetzt wird. Aufgrund der Hinzufügung des Rückgängig-Protokolls durchläuft die Write-Ahead-Protokollierung bei der Wiederherstellung nach einem Absturz die folgenden drei Phasen:

  • Analysephase (Analyse) : Diese Phase scannt das Protokoll vom letzten Prüfpunkt (Prüfpunkt, der als alle Änderungen verstanden werden kann, die beibehalten werden sollten, bevor dieser Punkt sicher auf der Festplatte abgelegt wurde), findet alle Transaktionen ohne Endaufzeichnung heraus und Bildet eine ausstehende Sammlung wiederhergestellter Transaktionen (im Allgemeinen einschließlich Transaktionstabelle und Dirty-Page-Tabelle).

  • Redo-Phase (Redo) : In dieser Phase wird der Verlauf (Repeat History) basierend auf der Sammlung der wiederherzustellenden Transaktionen, die in der Analysephase generiert wurden, erneut abgespielt, alle Protokolle mit Commit-Datensätzen ermittelt, auf die Festplatte geschrieben und nach dem Schreiben ein Eintrag hinzugefügt Ist der Vorgang abgeschlossen, beenden Sie die Aufzeichnung und entfernen Sie dann den wiederherzustellenden Transaktionssatz.

  • Rollback-Phase (Rückgängig) : In dieser Phase wird die verbleibende Sammlung von Wiederherstellungstransaktionen nach der Analyse- und Wiederherstellungsphase verarbeitet. Zu diesem Zeitpunkt handelt es sich beim Rest um Transaktionen, die zurückgesetzt werden müssen (Verlierer genannt) und gemäß den Informationen in zurückgesetzt werden das Rückgängig-Protokollieren dieser Vorgänge.

Operationen sowohl in der Redo-Phase als auch in der Rollback-Phase sollten idempotent gestaltet sein. Um eine hohe Leistung zu erzielen, erfordern die oben genannten drei Phasen zwangsläufig sehr umständliche Konzepte und Details (z. B. die spezifische Datenstruktur von Redo Log und Undo Log usw.).

Die Write-Ahead-Protokollierung ist Teil der ARIES-Theorie. Der gesamte Satz von ARIES bietet viele Vorteile wie Genauigkeit und hohe Leistung, diese gehen jedoch auch auf Kosten der Komplexität. Die Datenbank kann vier Kombinationen generieren, je nachdem, ob FORCE und STEAL zulässig sind. Aus Sicht der Optimierung der Festplatten-E/A ist die Leistung der Kombination aus NO-FORCE und STEAL zweifellos die höchste, aus Sicht der Algorithmusimplementierung und Protokolle, NO-FORCE Die Komplexität beim Hinzufügen einer STEAL-Kombination ist zweifellos am höchsten. Die spezifische Beziehung zwischen diesen vier Kombinationen und dem Undo-Log und Redo-Log ist in der folgenden Abbildung dargestellt:
Fügen Sie hier eine Bildbeschreibung ein

Isolation erreichen

Durch die Isolierung wird sichergestellt, dass die von den einzelnen Dingen gelesenen und geschriebenen Daten unabhängig voneinander sind und sich nicht gegenseitig beeinflussen. Aus der Definition geht hervor, dass Isolation eng mit Parallelität verbunden sein muss, denn wenn keine Parallelität besteht und alle Transaktionen seriell sind, ist keine Isolation erforderlich, oder ein solcher Zugriff weist eine natürliche Isolation auf. In Wirklichkeit ist es jedoch unmöglich, keine Parallelität zu haben. Wie kann ein serieller Datenzugriff unter Parallelität realisiert werden? Fast alle Programmierer werden antworten: Synchronisation sperren! Korrekte, moderne Datenbanken bieten die folgenden drei Arten von Sperren.

  • Schreibsperre : (Schreibsperre, auch exklusive Sperre genannt, eXclusive Sperre, abgekürzt als X-Sperre): Wenn die Daten über eine Schreibsperre verfügen, kann nur die Transaktion, die die Schreibsperre hält, in die Daten schreiben, und die Daten unterstützen das Schreiben gesperrt, andere Transaktionen können weder Daten schreiben noch Lesesperren verhängen.

  • Lesesperre (Lesesperre, auch Shared Lock, Shared Lock, abgekürzt S-Lock genannt): Mehrere Transaktionen können denselben Daten mehrere Lesesperren hinzufügen. Nachdem die Daten gelesen wurden, können keine Schreibsperren hinzugefügt werden, also andere Transaktionen Die Daten können nicht beschrieben, aber trotzdem gelesen werden. Wenn bei einer Transaktion mit einer Lesesperre nur eine Transaktion der Daten über eine Lesesperre verfügt, ist es zulässig, diese direkt auf eine Schreibsperre zu aktualisieren und dann die Daten zu schreiben.

  • Bereichssperre : Fügen Sie einem bestimmten Bereich direkt eine exklusive Sperre hinzu, und Daten in diesem Bereich können nicht geschrieben werden. Die folgende Anweisung ist ein typisches Beispiel für das Hinzufügen von Bereichssperren:

SELECT * FROM books WHERE price < 100 FOR UPDATE;

​Bitte beachten Sie den Unterschied zwischen „Der Bereich kann nicht geschrieben werden“ und „Ein Datenstapel kann nicht geschrieben werden“, dh verstehen Sie die Bereichssperre nicht als eine Reihe exklusiver Sperren. Nachdem die Bereichssperre hinzugefügt wurde, können nicht nur die vorhandenen Daten im Bereich nicht mehr geändert werden, sondern es können auch beliebige Daten im Bereich hinzugefügt oder gelöscht werden, was für einen Satz exklusiver Sperren nicht möglich ist.

Vier Isolationsstufen für lokale Transaktionen

Dirty Reads, Phantom Reads und nicht wiederholbare Reads können in Transaktionen unterschiedlicher Ebenen auftreten, wie in der folgenden Abbildung dargestellt.
Fügen Sie hier eine Bildbeschreibung ein

  • Dirty Read: Eine Transaktion greift auf Daten zu und ändert sie, die Transaktion wurde jedoch noch nicht festgeschrieben, und eine andere Transaktion liest die Daten ebenfalls, was zu Dirty Reads führt.

  • Nicht wiederholbares Lesen: Lesen Sie in derselben Transaktion dieselben Daten mehrmals. Beispielsweise liest die erste Transaktion dieselben Daten zweimal, aber die Änderung der zweiten Transaktion führt dazu, dass die von der ersten Transaktion zweimal gelesenen Daten inkonsistent sind.

  • Phantom-Lesen: Tritt auf, wenn eine Transaktion nicht unabhängig ausgeführt wird. Beispielsweise ändert die erste Transaktion alle Daten in der Tabelle, die zweite Transaktion ändert auch die Daten in der Tabelle und fügt eine Zeile hinzu, und dann stellt die erste Transaktion fest, dass die Tabelle noch unveränderte Datenzeilen enthält.

Serialisierbar

Serialisierbar: Serialisierter Zugriff bietet die höchste Isolationsstufe, und die höchste in ANSI/ISO SQL-92 definierte Isolationsstufe ist Serialisierbar. Die Serialisierung entspricht voll und ganz dem Verständnis normaler Programmierer für Datenwettbewerbssperren. Wenn die Leistungsoptimierung nicht berücksichtigt wird, können alle gelesenen und geschriebenen Daten in der Transaktion durch Hinzufügen von Lesesperren, Schreibsperren und Bereichssperren serialisiert werden. Lineisierung („das ist es“) ist ein vereinfachtes Verständnis, aber tatsächlich sehr kompliziert. Es muss in zwei Phasen unterteilt werden: Erweitern und Verkleinern, um die Beziehung zwischen Lesesperren, Schreibsperren und Daten zu behandeln, die als Zweiphasensperre (2PL) bezeichnet wird. Es ist jedoch absolut unmöglich, dass die Datenbank die Leistung nicht berücksichtigt. Die Parallelitätskontrolltheorie (Concurrency Control) bestimmt, dass der Grad der Isolation und die Parallelitätsfähigkeit in Konflikt miteinander stehen. Je höher die Isolation, desto geringer ist der Durchsatz bei gleichzeitigem Zugriff. Moderne Datenbanken bieten den Benutzern auf jeden Fall andere Isolationsstufen als die serialisierbare, sodass Benutzer die Optionen für die Isolationsstufe anpassen können. Der grundlegende Zweck besteht darin, dass Benutzer die Sperrmethode der Datenbank anpassen können, um ein Gleichgewicht zwischen Isolation und Durchsatz zu erreichen.

wiederholbare Lektüre

Wiederholbares Lesen: Wiederholbares Lesen fügt den an der Transaktion beteiligten Daten Lesesperren und Schreibsperren hinzu und hält sie bis zum Ende der Transaktion, fügt jedoch keine Bereichssperren hinzu. Der schwächere Teil des wiederholbaren Lesens im Vergleich zur Serialisierung ist das Problem der Phantom-Lesevorgänge (Phantom Reads), was bedeutet, dass während der Transaktionsausführung zwei identische Bereichsabfragen unterschiedliche Ergebnismengen erhalten. . Wenn Sie beispielsweise die Anzahl der Bücher im Fenix-Buchladen zählen möchten, die für weniger als 100 Yuan verkauft werden, wird die erste SQL-Anweisung unten ausgeführt:

SELECT count(1) FROM books WHERE price < 100					/* 时间顺序:1,事务: T1 */
INSERT INTO books(name,price) VALUES ('深入理解Java虚拟机',90)	/* 时间顺序:2,事务: T2 */
SELECT count(1) FROM books WHERE price < 100					/* 时间顺序:3,事务: T1 */

Gemäß den vorherigen Definitionen von Bereichssperren, Lesesperren und Schreibsperren ist dies zulässig, wenn diese SQL-Anweisung zweimal in derselben Transaktion ausgeführt wird und es zufällig eine andere Transaktion gibt, die eine Kopie von Büchern mit einem Wert von weniger als 100 Yuan einfügt , dann erhalten die beiden gleichen Abfragen unterschiedliche Ergebnisse. Der Grund dafür ist, dass es keine Bereichssperre für wiederholbares Lesen gibt, um das Einfügen neuer Daten in den Bereich zu verhindern. Dies ist eine Transaktion, die von anderen Transaktionen beeinflusst wird und die Leistung der Isolation zerstört.

Es sei daran erinnert, dass die Einführung hier auf der ARIES-Theorie basiert und die spezifische Datenbank nicht unbedingt in voller Übereinstimmung mit der Theorie implementiert werden muss. Ein Beispiel ist, dass die Standardisolationsstufe von MySQL/InnoDB wiederholbares Lesen ist, aber das Phantomleseproblem bei schreibgeschützten Transaktionen vollständig vermieden werden kann (da InnoDB MVCC verwendet, ist die wiederholbare Lesestufe nur kleiner oder gleich dem Datensatz). der aktuellen Transaktions-ID finden Sie unten eine Einführung in MVCC. Beispielsweise verfügt die Transaktion T1 im obigen Beispiel nur über Abfrageanweisungen, bei denen es sich um eine schreibgeschützte Transaktion handelt, sodass das Problem im Beispiel in MySQL nicht auftritt. Bei den Lese- und Schreibtransaktionen weist MySQL jedoch immer noch Phantomleseprobleme auf. Wenn beispielsweise die Transaktion T1 im Beispiel neue Bücher in andere Transaktionen einfügt, werden alle Bücher unter 100 Yuan umbenannt, anstatt die Menge erneut abzufragen . Betroffen von neu eingefügten Büchern.

gelesen verpflichtet

Read Committed (Read Committed): Die Lese-Committed-Schreibsperre für die an der Transaktion beteiligten Daten bleibt bis zum Ende der Transaktion bestehen, die hinzugefügte Lesesperre wird jedoch sofort nach Abschluss des Abfragevorgangs freigegeben. Der schwächere Teil des festgeschriebenen Lesevorgangs im Vergleich zum wiederholbaren Lesevorgang ist das Problem nicht wiederholbarer Lesevorgänge (Non-Repeatable Reads), was bedeutet, dass während der Ausführung einer Transaktion zwei Abfragen für dieselben Daten unterschiedliche Ergebnisse liefern. Beispielsweise möchte der Autor den Preis des Buches „In-Depth Understanding of Java Virtual Machine“ im Buchladen von Fenix ​​erhalten und führt außerdem zwei SQL-Anweisungen aus. Zwischen der Ausführung dieser beiden Anweisungen ändert eine weitere Transaktion den Preis dieses Buchs ., Passen Sie den Preis des Buches von 90 Yuan auf 110 Yuan an, wie in der folgenden SQL gezeigt:

SELECT * FROM books WHERE id = 1;   						/* 时间顺序:1,事务: T1 */
UPDATE books SET price = 110 WHERE id = 1; COMMIT;			/* 时间顺序:2,事务: T2 */
SELECT * FROM books WHERE id = 1; COMMIT;   				/* 时间顺序:3,事务: T1 */

Wenn die Isolationsstufe „Lesen festgeschrieben“ ist, sind die Ergebnisse der beiden wiederholten Ausführungen der Abfrage unterschiedlich, da die Isolationsstufe „Lesen festgeschrieben“ während des gesamten Transaktionszyklus keine Lesesperre aufweist und nicht verhindern kann, dass sich die gelesenen Daten ändern. Zu diesem Zeitpunkt Die Aktualisierungsanweisung in Transaktion T2 kann sofort erfolgreich übermittelt werden, was auch ein Zeichen dafür ist, dass eine Transaktion von anderen Transaktionen beeinflusst wird und die Isolation aufgehoben wird. Wenn die Isolationsstufe wiederholbares Lesen ist, kann Transaktion T2 die Schreibsperre nicht erhalten, da die Daten durch Transaktion T1 gesperrt wurden und nicht sofort nach dem Lesen freigegeben werden, und die Aktualisierung wird blockiert, bis Transaktion T1 festgeschrieben oder zurückgegeben wird. Anschließend senden rollt.

unverbindlich lesen

Read Uncommitted (Read Uncommitted): Read Uncommitted fügt den an der Transaktion beteiligten Daten nur eine Schreibsperre hinzu, die bis zum Ende der Transaktion anhält, fügt jedoch überhaupt keine Lesesperre hinzu. Der schwächere Aspekt des nicht festgeschriebenen Lesens im Vergleich zum festgeschriebenen Lesen ist das Problem schmutziger Lesevorgänge (Dirty Reads), was bedeutet, dass während der Ausführung einer Transaktion eine Transaktion nicht festgeschriebene Daten von einer anderen Transaktion liest. Beispielsweise war der Autor der Ansicht, dass die Preiserhöhung von 90 Yuan auf 110 Yuan in „Vertieftes Verständnis der Java Virtual Machine“ eine Handlung war, die den Interessen der Verbraucher schadete, und gab eine Aktualisierungserklärung ab, um den Preis wieder auf 90 Yuan zu ändern Yuan. Bevor ich die Transaktion einreichte, sagten meine Kollegen, dass dies nicht der Fall sei. Der zufällige Preisanstieg wird durch die Erhöhung der Druckkosten verursacht. Wenn Sie es für 90 Yuan verkaufen, verlieren Sie Geld, daher hat der Autor die Transaktion sofort rückgängig gemacht. Die Szene ist wie in der folgenden SQL dargestellt:

SELECT * FROM books WHERE id = 1;   						/* 时间顺序:1,事务: T1 */
/* 注意没有COMMIT */
UPDATE books SET price = 90 WHERE id = 1;					/* 时间顺序:2,事务: T2 */
/* 这条SELECT模拟购书的操作的逻辑 */
SELECT * FROM books WHERE id = 1;			  				/* 时间顺序:3,事务: T1 */
ROLLBACK;			  										/* 时间顺序:4,事务: T2 */

Allerdings wurden nach der vorherigen Preisrevision bei Transaktion T1 mehrere Exemplare zu einem Preis von 90 Yuan verkauft. Der Grund dafür ist, dass beim nicht festgeschriebenen Lesen den Daten überhaupt keine Lesesperre hinzugefügt wird, was stattdessen das Lesen von Daten ermöglicht, die von anderen Transaktionen gesperrt wurden, d. h. den Ergebnissen, die durch die beiden Abfrageanweisungen in der obigen Transaktion T1 erhalten wurden nicht das gleiche. Wenn Sie das Wort „stattdessen“ in diesem Satz nicht verstehen können, lesen Sie bitte die Definition der Schreibsperre noch einmal: Die Schreibsperre verhindert, dass andere Transaktionen Lesesperren anwenden, anstatt Transaktionen das Lesen von Daten zu verbieten, wenn Transaktion T1 überhaupt Daten liest Wenn keine Lesesperre hinzugefügt werden muss, werden die nicht festgeschriebenen Daten der Transaktion T2 sofort von der Transaktion T1 gelesen. Dies bedeutet auch, dass eine Transaktion von anderen Transaktionen beeinflusst wird und die Isolation zerstört wird. Wenn die Isolationsstufe lesefestgeschrieben ist, kann die zweite Abfrage von Transaktion T1 die Lesesperre nicht erhalten, da die Transaktion T2 eine Schreibsperre für die Daten hält, und die lesefestgeschriebene Stufe erfordert das Hinzufügen einer Lesesperre vor dem Lesen der Daten Die Abfrage in T1 wird blockiert, bis die Transaktion T2 festgeschrieben oder zurückgesetzt wird, um das Ergebnis zu erhalten.

Tatsächlich sind Probleme wie unterschiedliche Isolationsstufen, Phantom-Lesevorgänge, nicht wiederholbare Lesevorgänge und schmutzige Lesevorgänge nur oberflächliche Phänomene. Sie sind das Ergebnis der kombinierten Anwendung verschiedener Sperren zu unterschiedlichen Sperrzeiten. Die Verwendung von Sperren als Mittel zur Isolation ist die Leistung der Datenbank. Die Hauptursache für unterschiedliche Isolationsstufen.

Grundprinzipien von MVCC

Die vier Isolationsstufen haben ein weiteres gemeinsames Merkmal: Probleme wie Phantom-Lesevorgänge, nicht wiederholbare Lesevorgänge und schmutzige Lesevorgänge werden alle dadurch verursacht, dass eine Transaktion von einer anderen Transaktion betroffen ist, die während des Datenlesevorgangs Daten schreibt. Für das Isolationsproblem „eine Transaktion lesen + eine weitere Transaktion schreiben“ wurde in den letzten Jahren ein sperrenfreies Optimierungsschema namens „Multi-Version Concurrency Control“ (MVCC) von gängigen kommerziellen Datenbanken weithin übernommen. MVCC ist eine Leseoptimierungsstrategie und bedeutet „sperrfrei“, dass beim Lesen keine Sperre erforderlich ist. Die Grundidee von MVCC besteht darin, dass jede Änderung an der Datenbank die Daten nicht direkt überschreibt, sondern eine neue Version der Kopie generiert, die mit der alten Version koexistiert, um den Zweck zu erreichen, beim Lesen überhaupt nicht zu sperren . In diesem Satz ist „Version“ ein Schlüsselwort. Die Version kann so verstanden werden, dass es in jeder Datensatzzeile der Datenbank zwei unsichtbare Felder gibt: CREATE_VERSION und DELETE_VERSION. Die in diesen beiden Feldern aufgezeichneten Werte sind Transaktions-IDs. Transaktions-IDs Ist ein global streng steigender Wert und schreibt dann Daten gemäß den folgenden Regeln.

  • Daten einfügen: CREATE_VERSION zeichnet die Transaktions-ID zum Einfügen von Daten auf und DELETE_VERSION ist leer.
  • Beim Löschen von Daten: DELETE_VERSION zeichnet die Transaktions-ID zum Löschen von Daten auf und CREATE_VERSION ist leer.
  • Beim Ändern von Daten: Behandeln Sie die geänderten Daten als eine Kombination aus „Löschen alter Daten und Einfügen neuer Daten“, dh kopieren Sie zuerst die Originaldaten, zeichnen Sie die Transaktions-ID der geänderten Daten in DELETE_VERISON der Originaldaten auf und lassen Sie CREATA_VERSION leer . Die CREATE_VERSION der kopierten neuen Daten zeichnet die Transaktions-ID der geänderten Daten auf und die DELETE_VERSION ist leer.

Wenn zu diesem Zeitpunkt eine andere Transaktion die geänderten Daten lesen möchte, entscheidet sie entsprechend der Isolationsstufe, welche Version der Daten gelesen werden soll.

Die Isolationsstufe ist wiederholbares Lesen : Lesen Sie immer den Datensatz, dessen CREATE_VERSION kleiner oder gleich der aktuellen Transaktions-ID ist. Unter dieser Voraussetzung wird die neueste Version (mit der größten Transaktions-ID) verwendet, wenn noch mehrere Versionen der Daten vorhanden sind .
Die Isolationsstufe ist read commit : Nehmen Sie immer die neueste Version, also den Datensatz der zuletzt festgeschriebenen Version.
Für die anderen beiden Isolationsstufen muss MVCC nicht verwendet werden, da die Originaldaten durch nicht festgeschriebenes Lesen direkt geändert werden können und andere Transaktionen sie beim Anzeigen der Daten sofort sehen können und überhaupt kein Versionsfeld erforderlich ist. Die ursprüngliche Semantik der Serialisierung besteht darin, die Lesevorgänge anderer Transaktionen zu blockieren, und MVCC dient der sperrenfreien Optimierung beim Lesen, daher wird es natürlich nicht zusammen verwendet.

MVCC ist nur für „Lesen + Schreiben“-Szenarien optimiert. Wenn zwei Transaktionen gleichzeitig Daten ändern, also „Schreiben + Schreiben“, gibt es nicht viel Raum für Optimierung. Derzeit ist Sperren fast die einzig mögliche Lösung Es gibt ein wenig Raum für Diskussionen darüber, ob die Sperrstrategie „Optimistisches Sperren“ oder „Pessimistisches Sperren“ ist . Das vom Autor oben beschriebene Sperren ist eine pessimistische Sperrstrategie. Das heißt, wenn Sie nicht zuerst sperren und dann auf die Daten zugreifen, wird es definitiv Probleme geben. Im Gegensatz dazu geht die optimistische Sperrstrategie davon aus, dass der Datenwettbewerb zwischen Transaktionen zufällig ist und kein Wettbewerb üblich ist. Auf diese Weise sollte die Sperrung nicht zu Beginn erfolgen, sondern Abhilfemaßnahmen gefunden werden, wenn Wettbewerb auftritt. Diese Art des Denkens wird als „Optimistische Parallelitätskontrolle“ (Optimistic Concurrency Control, OCC) bezeichnet, aber der Autor erinnert daran, dass es keinen Grund gibt, abergläubisch zu sein, wenn man sagt, dass optimistische Sperren schneller sind als pessimistische Sperren. Dies hängt ausschließlich von der Intensität ab Wettbewerb. Wenn der Wettbewerb hart ist, ist das optimistische Sperren in diesem Fall langsamer.

Weltgeschehen

Das Gegenteil der lokalen Transaktion ist die globale Transaktion (Global Transaction), die in einigen Materialien auch als externe Transaktion (External Transaction) bezeichnet wird. In diesem Abschnitt ist die globale Transaktion auf einen einzelnen Dienst beschränkt, der mehrere Datenquellen verwendet. Szenariotransaktion Lösung. Bitte beachten Sie, dass die echte globale Transaktion theoretisch nicht der Einschränkung eines „einzelnen Dienstes“ unterliegt. Eine konsistente Transaktionsverarbeitungslösung ist äußerst ungeeignet für Fälle, in denen mehrere Knoten gegenseitig ihre Dienste aufrufen (typischerweise das aktuelle Microservice-System). Heutzutage ist dies der Fall Wird fast nur bei Gelegenheiten mit einem einzigen Dienst und mehreren Datenquellen verwendet. Um Verwechslungen mit der später eingeführten schwach konsistenten Transaktionsverarbeitungsmethode zu vermeiden, die ACID aufgibt, wurde der Umfang der globalen Transaktion hier reduziert und nachfolgende Transaktionen, die mehrere Dienste und mehrere Daten umfassen Quellen werden als „verteilte Transaktionen“ bezeichnet.

XA-Protokoll

Um das Konsistenzproblem verteilter Transaktionen zu lösen, schlug die X/Open-Organisation (später in The Open Group fusioniert) 1991 eine Transaktionsverarbeitungsarchitektur namens X/Open XA vor (XA ist die Abkürzung für eXtended Architecture). Der Kerninhalt besteht darin, die Kommunikationsschnittstelle zwischen dem globalen Transaktionsmanager (Transaktionsmanager, der zur Koordinierung globaler Transaktionen verwendet wird) und dem lokalen Ressourcenmanager (Ressourcenmanager, der zur Steuerung lokaler Transaktionen verwendet wird) zu definieren. Die XA-Schnittstelle ist bidirektional und kann eine Kommunikationsbrücke zwischen einem Transaktionsmanager und mehreren Ressourcenmanagern (Ressourcenmanagern) bilden. Durch die Koordinierung der konsistenten Aktionen mehrerer Datenquellen kann die einheitliche Übermittlung oder das einheitliche Rollback globaler Transaktionen realisiert werden. Jetzt die Namen Daraus stammen XADataSource und XAResource, die wir gelegentlich in Java-Code sehen.

XA ist jedoch keine technische Java-Spezifikation (XA schlug vor, dass es zu diesem Zeitpunkt noch kein Java gab), sondern eine Reihe sprachunabhängiger allgemeiner Spezifikationen, sodass Java speziell die Java-Transaktions-API JSR 907 definiert, basierend auf der Implementierung des XA-Modus in Java-Sprache Dies ist der Standard für die globale Transaktionsverarbeitung, den wir heute als JTA kennen. Die beiden Hauptschnittstellen von JTA sind:

  • Die Schnittstelle des Transaktionsmanagers: javax.transaction.TransactionManager. Dieser Satz von Schnittstellen wird vom Java EE-Server verwendet, um Containertransaktionen bereitzustellen (der Container ist automatisch für die Transaktionsverwaltung verantwortlich). Er stellt außerdem einen weiteren Satz von javax.transaction.UserTransaction-Schnittstellen zum manuellen Öffnen, Festschreiben und Zurücksetzen von Transaktionen über das Programm bereit Code.
  • Die Ressourcendefinitionsschnittstelle, die der XA-Spezifikation entspricht: javax.transaction.xa.XAResource. Wenn eine Ressource (JDBC, JMS usw.) JTA unterstützen möchte, muss sie nur die Methode in der XAResource-Schnittstelle implementieren.

JTA war ursprünglich eine Technologie in Java EE. Unter normalen Umständen sollte es von Java EE-Containern wie JBoss, WebSphere und WebLogic unterstützt werden, aber jetzt haben Bittronix, Atomikos und JBossTM (früher Arjuna genannt) JTA-Schnittstellen in dieser Form implementiert von JAR-Paketen. , genannt JOTM (Java Open Transaction Manager), ermöglicht uns die Verwendung von JTA in Java SE-Umgebungen wie Tomcat und Jetty.

Lassen Sie uns nun eine weitere Annahme zum Szenariobeispiel in diesem Kapitel treffen: Wenn sich die Benutzer, Händler und Lager des Buchladens in unterschiedlichen Datenbanken befinden und andere Bedingungen immer noch dieselben sind wie zuvor, was passiert dann mit der Situation? Wenn Sie hinzufügen, dass Sie normalerweise mit deklarativen Transaktionen codieren, sieht es möglicherweise genauso aus wie lokale Transaktionen. Sie sind alle mit @Transactional-Anmerkungen gekennzeichnet. Wenn es jedoch mit programmgesteuerten Transaktionen implementiert wird, können Sie den Unterschied beim Schreiben erkennen. Der Pseudo-Code sieht folgendermaßen aus :

public void buyBook(PaymentBill bill) {
    
    
    userTransaction.begin();
    warehouseTransaction.begin();
    businessTransaction.begin();
	try {
    
    
        userAccountService.pay(bill.getMoney());
        warehouseService.deliver(bill.getItems());
        businessAccountService.receipt(bill.getMoney());
        userTransaction.commit();
        warehouseTransaction.commit();
        businessTransaction.commit();
	} catch(Exception e) {
    
    
        userTransaction.rollback();
        warehouseTransaction.rollback();
        businessTransaction.rollback();
	}
}

Zweiphasen-Commit

Aus dem Code ist ersichtlich, dass der Zweck des Programms darin besteht, drei Transaktions-Commits durchzuführen. Tatsächlich kann der Code jedoch nicht so geschrieben werden. Stellen Sie sich vor, wenn in businessTransaction.commit () ein Fehler auftritt, wird der Code an übertragen Der Catch-Block wird ausgeführt. Zu diesem Zeitpunkt wurden userTransaction und WarehouseTransaction übermittelt, und der Aufruf der rollback()-Methode hat keinen Erfolg. Dies führt dazu, dass ein Teil der Daten übermittelt und der andere Teil zurückgesetzt wird Die Konsistenz der gesamten Transaktion kann nicht garantiert werden. Um dieses Problem zu lösen, teilt XA den Transaktions-Commit in einen zweiphasigen Prozess auf:

  • Vorbereitungsphase: Auch Abstimmungsphase genannt. In dieser Phase fragt der Koordinator alle Teilnehmer der Transaktion, ob sie zur Übermittlung bereit sind. Wenn die Teilnehmer zur Übermittlung bereit sind, antworten sie mit „Vorbereitet“, andernfalls antworten sie mit „Nicht vorbereitet“. . Der hier erwähnte Vorbereitungsvorgang ist nicht derselbe wie der in der menschlichen Sprache allgemein verstandene Vorbereitungsvorgang. Für die Datenbank besteht der Vorbereitungsvorgang darin, den Inhalt aller Transaktions-Festschreibungsvorgänge im Redo-Protokoll aufzuzeichnen, was sich vom tatsächlichen Festschreiben im lokalen Protokoll unterscheidet Transaktion Es ist nur so, dass der letzte Commit-Datensatz vorerst nicht geschrieben wird, was bedeutet, dass die Isolation nicht sofort nach der Beibehaltung der Daten aufgehoben wird, das heißt, die Sperre wird weiterhin gehalten, um die Isolation der Daten von anderen Nicht-Daten aufrechtzuerhalten. Transaktionsbeobachter.
  • Commit-Phase: Wird auch als Ausführungsphase bezeichnet. Wenn der Koordinator die von allen Transaktionsteilnehmern in der vorherigen Phase beantwortete vorbereitete Nachricht erhält, behält er zunächst den Transaktionsstatus lokal als Commit bei und sendet nach Abschluss des Vorgangs die Commit-Anweisung an alle Teilnehmer. alle Teilnehmer führen den Commit-Vorgang sofort aus; andernfalls, wenn ein Teilnehmer auf die Nachricht „Nicht vorbereitet“ antwortet oder ein Teilnehmer nach einer Zeitüberschreitung nicht antwortet, behält der Koordinator seinen eigenen Transaktionsstatus als „Abort“ bei und meldet allen Teilnehmern den Befehl „Abort“. , und der Teilnehmer führt sofort den Rollback-Vorgang aus. Für die Datenbank sollte der Festschreibungsvorgang in dieser Phase sehr einfach sein. Es handelt sich lediglich um einen dauerhaften Festschreibungsdatensatz, der normalerweise schnell abgeschlossen werden kann. Erst wenn der Abbruchbefehl empfangen wird, müssen die festgeschriebenen Daten gemäß dem Rollback bereinigt werden log. Dies kann ein relativ schwerer Arbeitsvorgang sein.

Die beiden oben genannten Prozesse werden als „2-Phasen-Commit“-Protokoll (2-Phasen-Commit, 2PC) bezeichnet und erfordern einige weitere Voraussetzungen, um die Konsistenz erfolgreich sicherzustellen.

  1. Es muss davon ausgegangen werden, dass das Netzwerk für den kurzen Zeitraum der Commit-Phase zuverlässig ist, d. h. dass während der Commit-Phase keine Nachrichten verloren gehen. Gleichzeitig wird auch davon ausgegangen, dass im gesamten Prozess der Netzwerkkommunikation kein Fehler auftritt, dh die letzte Nachricht kann verloren gehen, aber die falsche Nachricht wird nicht zugestellt. Das Entwurfsziel von XA besteht nicht darin Lösen Sie Probleme wie byzantinische Generäle. Der Fehler in der Abstimmungsphase beim zweistufigen Commit kann behoben (zurückgesetzt) ​​werden, während der Fehler in der Commit-Phase nicht behoben werden kann (das Ergebnis des Commits oder Rollbacks wird nicht mehr geändert, und der abgestürzte Knoten kann nur geändert werden). wiederhergestellt werden kann), daher braucht diese Phase Zeit und sollte so kurz wie möglich sein. Dies ist auch eine Überlegung, um Netzwerkrisiken so weit wie möglich zu kontrollieren.
  2. Es muss davon ausgegangen werden, dass Knoten, die aufgrund von Netzwerkpartitionen, Maschinenabstürzen oder anderen Gründen getrennt wurden, irgendwann wiederhergestellt werden und nicht dauerhaft getrennt werden. Da das vollständige Redo-Protokoll in der Vorbereitungsphase geschrieben wurde, kann die verlorene Maschine nach der Wiederherstellung die vorbereiteten, aber nicht festgeschriebenen Transaktionsdaten aus dem Protokoll ermitteln und dann den Status der Transaktion an den Koordinator abfragen Bestimmen Sie, ob der nächste Schritt ein Commit- oder Rollback-Vorgang sein soll.

Bitte beachten Sie, dass die oben genannten Koordinatoren und Teilnehmer in der Regel von der Datenbank selbst abgespielt werden, ohne dass die Anwendung eingreifen muss. Der Koordinator wird in der Regel unter den Teilnehmern gewählt und die Anwendung übernimmt nur die Rolle des Clients gegenüber der Datenbank. Die Interaktionssequenz des zweistufigen Commits ist in der folgenden Abbildung dargestellt.
Fügen Sie hier eine Bildbeschreibung ein
Das Prinzip des zweistufigen Commits ist einfach und nicht schwer zu implementieren, es gibt jedoch einige sehr erhebliche Nachteile:

  • Einzelpunktproblem : Der Koordinator spielt eine zentrale Rolle bei der zweistufigen Übermittlung. Der Koordinator kann über einen Timeout-Mechanismus verfügen, wenn er auf die Antwort des Teilnehmers wartet, sodass der Teilnehmer aussteigen kann. Der Teilnehmer kann jedoch keine Timeout-Verarbeitung durchführen, während er auf die Antwort des Koordinators wartet Anweisung. Sobald nicht einer der Teilnehmer ausfällt, sondern der Koordinator, sind alle Teilnehmer betroffen. Wenn sich der Koordinator nicht erholt hat und keinen normalen Commit- oder Rollback-Befehl gesendet hat, müssen alle Teilnehmer warten.
  • Leistungsprobleme : Während des zweistufigen Übermittlungsprozesses sind alle Teilnehmer gleichbedeutend mit der Einbindung in ein einheitliches Planungsganzes. Während dieses Zeitraums werden zwei Remote-Serviceaufrufe und drei Datenpersistenzen durchgeführt (Redo-Protokolle werden in der Vorbereitungsphase geschrieben, und der Koordinator macht sie dauerhaft). Zustand In der Festschreibungsphase wird das Protokoll in den Festschreibungsdatensatz geschrieben, und der gesamte Prozess wird fortgesetzt, bis der langsamste Verarbeitungsvorgang im Teilnehmercluster endet, was dazu führt, dass die Leistung des zweistufigen Festschreibens normalerweise schlecht ist.
  • Konsistenzrisiko : Wie bereits erwähnt, gibt es Voraussetzungen für die Einrichtung eines zweiphasigen Commits. Wenn die Annahmen zur Netzwerkstabilität und zur Wiederherstellungsfähigkeit nach Ausfallzeiten nicht erfüllt sind, können dennoch Konsistenzprobleme auftreten. Über die Fähigkeit zur Wiederherstellung nach Ausfallzeiten muss nicht gesprochen werden. 1985 schlugen Fischer, Lynch und Paterson das „FLP-Unmöglichkeitsprinzip“ vor, das bewies, dass es kein verteiltes Protokoll gibt, wenn die Ausfallzeit am Ende nicht wiederhergestellt werden kann Das kann richtig zu einem Konsens führen. Sex-Ergebnisse. Dieses Prinzip ist eine Theorie mit dem gleichen Namen wie „CAP kann nicht beide Prinzipien haben“ in verteilter Form. Das durch die Netzwerkstabilität verursachte Konsistenzrisiko bedeutet, dass die Übermittlungsperiode zwar sehr kurz ist, aber immer noch eine klare Gefahr darstellt. Stellen Sie sicher, dass der Transaktionsstatus übermittelt werden kann. Der Koordinator behält zunächst den Transaktionsstatus bei und übermittelt seine eigene Transaktion . Wenn das Netzwerk zu diesem Zeitpunkt plötzlich getrennt wird und es nicht mehr möglich ist, über das Netzwerk einen Commit-Befehl an alle Teilnehmer zu erteilen, gehen einige Daten verloren. (des Koordinators) wurde festgeschrieben, aber ein Teil der Daten (von der Teilnehmer) wurde weder festgeschrieben noch zurückgesetzt, was zu Dateninkonsistenzen führt.

dreistufige Einreichung

Um einige der Mängel des Zwei-Phasen-Commit-Protokolls zu lindern, insbesondere das Einpunktproblem des Koordinators und das Leistungsproblem in der Vorbereitungsphase, wurde ein „Drei-Phasen-Commit“-Protokoll (3 Phase Commit, 3PC) entwickelt anschließend weiterentwickelt. Das dreistufige Commit unterteilt die ursprüngliche zweistufige Commit-Vorbereitungsphase in zwei Phasen, die CanCommit und PreCommit heißen, und die Commit-Phase wird in DoCommit-Phase umbenannt. Darunter stellt das neu hinzugefügte CanCommit eine Abfragephase dar. Der Koordinator ermöglicht es jeder teilnehmenden Datenbank, zu bewerten, ob die Transaktion gemäß ihrem eigenen Status wahrscheinlich erfolgreich abgeschlossen wird. Der Grund für die Aufteilung der Vorbereitungsphase in zwei Teile besteht darin, dass es sich bei dieser Phase um eine anspruchsvolle Operation handelt. Sobald der Koordinator eine Nachricht zum Starten der Vorbereitung sendet, beginnt jeder Teilnehmer sofort mit dem Schreiben von Redo-Protokollen und die beteiligten Datenressourcen werden gesperrt. , wenn Wenn ein bestimmter Teilnehmer erklärt, dass die Einreichung zu diesem Zeitpunkt nicht abgeschlossen werden kann, bedeutet dies, dass jeder eine Runde nutzloser Arbeit geleistet hat. Wenn Sie also eine Anfragephase hinzufügen und eine positive Antwort erhalten, sind Sie zuversichtlicher, dass die Transaktion erfolgreich durchgeführt werden kann. kleiner werden. Daher ist in dem Szenario, in dem die Transaktion zurückgesetzt werden muss, die Leistung der dreistufigen Methode normalerweise viel besser als die der zweistufigen Methode, aber in dem Szenario, in dem die Transaktion normal übermittelt werden kann, ist die Leistung von Beide sind immer noch sehr schlecht, selbst die dreistufige Methode. Aufgrund einer weiteren Anfrage war sie etwas schlechter.

Dies liegt auch daran, dass die Wahrscheinlichkeit eines Rollbacks von Transaktionsfehlern geringer wird. Wenn der Koordinator beim dreistufigen Commit nach der PreCommit-Phase ausfällt, d Transaktionen festschreiben, anstatt Transaktionen rückgängig zu machen oder weiter zu warten, was gleichbedeutend damit ist, das Risiko eines einzelnen Problempunkts für den Koordinator zu vermeiden. Die Operationssequenz des dreistufigen Commits ist in der folgenden Abbildung dargestellt.
Fügen Sie hier eine Bildbeschreibung ein

Aus dem obigen Prozess ist ersichtlich, dass das dreistufige Commit das Einzelpunktproblem und das Leistungsproblem beim Rollback verbessert hat, das Konsistenzrisikoproblem jedoch nicht verbessert hat und das Risiko, dem es in dieser Hinsicht ausgesetzt ist, sogar geringfügig ist sind erhöhte. Nach dem Eintritt in die PreCommit-Phase lautet der vom Koordinator beispielsweise ausgegebene Befehl nicht „Ack“, sondern „Abort“. Wenn zu diesem Zeitpunkt aufgrund von Netzwerkproblemen einige Teilnehmer den Abbruchbefehl vom Koordinator bis zum Timeout nicht erhalten, werden diese Teilnehmer fälschlicherweise dies tun Bestätigen Sie die Transaktion, wodurch das Problem der Dateninkonsistenz zwischen verschiedenen Teilnehmern entsteht.

gemeinsame Angelegenheiten

Im Gegensatz zur Verwendung mehrerer Datenquellen durch einen einzelnen Dienst, die in „Globale Transaktionen“ erläutert wird, bezieht sich „Share Transaction“ auf mehrere Dienste, die dieselbe Datenquelle gemeinsam nutzen. Hier muss noch einmal auf den Unterschied zwischen „Datenquelle“ und „Datenbank“ hingewiesen werden: Eine Datenquelle bezieht sich auf ein logisches Gerät, das Daten bereitstellt, und entspricht nicht unbedingt einzeln einem physischen Gerät. Der am häufigsten verwendete Modus bei der Bereitstellung von Anwendungsclustern besteht darin, denselben Programmsatz auf mehreren Middleware-Servern bereitzustellen, um mehrere Replikatinstanzen zu bilden und den Verkehrsdruck zu teilen. Obwohl sie mit derselben Datenbank verbunden sind, verfügt jeder Knoten über eine eigene exklusive Datenquelle. Normalerweise ist die Middleware für den Programmcode in Form von JNDI offen. In diesem Fall ist der Datenzugriff aller Replikatinstanzen völlig unabhängig und überschneidet sich nicht, und jeder Knoten verwendet weiterhin die einfachste lokale Transaktion. In diesem Abschnitt wird das Szenario erörtert, in dem mehrere Dienste geschäftliche Schnittpunkte haben. Als konkretes Beispiel wird im Szenario des Buchladens von Fenix ​​angenommen, dass Benutzerkonten, Händlerkonten und Produktlager alle in derselben Datenbank gespeichert sind, unabhängige Mikrodienste jedoch Wird in jedem Benutzer-, Händler- und Lagerbereich eingesetzt. Zu diesem Zeitpunkt läuft der Geschäftsvorgang eines Buchkaufs über drei Mikrodienste, und alle müssen Daten in der Datenbank ändern. Wenn wir unterschiedliche Datenquellen direkt als unterschiedliche Datenbanken betrachten, sind sowohl die im vorherigen Abschnitt erwähnten globalen Transaktionen als auch die im nächsten Abschnitt erwähnten verteilten Transaktionen möglich. Für jede Datenquellenverbindung dieser Art handelt es sich jedoch um einen Sonderfall Die gleiche physische Datenbank und gemeinsame Transaktionen können zu einer weiteren Möglichkeit werden, die Leistung zu verbessern und die Komplexität zu verringern. Natürlich ist es auch wahrscheinlich, dass es sich um eine falsche Anforderung handelt.

Eine theoretisch mögliche Lösung besteht darin, jeden Dienst die Datenbankverbindung direkt gemeinsam nutzen zu lassen. Es ist nicht schwierig, die Datenbankverbindung zwischen verschiedenen Persistenztools (JDBC, ORM, JMS usw.) im selben Anwendungsprozess gemeinsam zu nutzen. Einige Middleware-Server, z WebSphere wird eine integrierte Funktion für „gemeinsam nutzbare Verbindungen“ haben, die dies speziell unterstützt. Die Voraussetzung für diese Art der Freigabe ist jedoch, dass sich alle Benutzer der Datenquelle im selben Prozess befinden. Da die Datenbankverbindung auf der Netzwerkverbindung basiert, ist sie an die IP-Adresse und die Portnummer gebunden. Im wahrsten Sinne des Wortes „Verschiedene Dienstknoten teilen sich die Datenbankverbindung“ ist schwer zu erreichen. Um gemeinsame Transaktionen zu erreichen, muss daher eine Zwischenrolle als „Transaktionsserver“ hinzugefügt werden. Ob Benutzerdienst, Händlerdienst oder Lagerdienst, sie alle beschäftigen sich damit die Datenbank über denselben Transaktionsserver. Wenn die externe Schnittstelle des Transaktionsservers gemäß der JDBC-Spezifikation implementiert ist, kann sie als unabhängiger Remote-Datenbankverbindungspool von jedem Dienst oder direkt als Datenbankagent betrachtet werden. Zu diesem Zeitpunkt können die von den drei Diensten gesendeten Transaktionsanforderungen mit derselben Datenbank auf dem Transaktionsserver verbunden und über lokale Transaktionen abgeschlossen werden. Beispielsweise verwendet der Transaktionsserver gemäß derselben von verschiedenen Dienstknoten gesendeten Transaktions-ID dieselbe Datenbankverbindung, um Transaktionen zu verarbeiten, die mehrere Dienste umfassen, wie in der folgenden Abbildung dargestellt. Der Grund, warum betont wird, dass dies theoretisch machbar ist, liegt darin, dass Diese Lösung hat nichts mit
Fügen Sie hier eine Bildbeschreibung ein
der tatsächlichen Produktion zu tun. Die Richtung des Drucks im System ist entgegengesetzt. Die Datenbank in einem Service-Cluster ist der am stärksten betroffene Bereich mit dem größten Druck und am schwierigsten zu skalieren und zu erweitern. Daher gibt es in der Realität solche nur Datenbank-Proxys wie ProxySQL und MaxScale, die zum Ausgleich der Last mehrerer Datenbankinstanzen verwendet werden. (Tatsächlich kommt die Verwendung von ProxySQL als Proxy für eine einzelne Datenbank und die Aktivierung von Verbindungsmultiplex bereits der oben erwähnten Transaktionsserverlösung nahe), und es gibt sie fast Keine Transaktionsdienstagenten, die wiederum eine Datenbank vertreten, um die Transaktionskoordination für mehrere Anwendungen bereitzustellen. Dies ist auch der Grund, warum es sich eher um eine Pseudoanforderung handelt. Wenn Sie ausreichende Gründe dafür haben, dass mehrere Microservices die Datenbank gemeinsam nutzen, müssen Sie einen tragfähigeren Grund finden, um dem Team zu erklären, was der Zweck der Aufteilung von Microservices ist. OK.

In der täglichen Entwicklung gibt es eine häufigere Variante der oben genannten Lösung: die Verwendung eines Nachrichtenwarteschlangenservers anstelle eines Transaktionsservers. Wenn die Dienste von Benutzern, Händlern und Lagern Geschäfte betreiben, werden alle Änderungen an der Datenbank über Nachrichten an den Nachrichtenwarteschlangenserver übertragen und über Nachrichtenkonsumenten einheitlich verarbeitet, um dauerhafte Vorgänge zu realisieren, die durch lokale Transaktionen garantiert werden. Dies wird als „Nachrichtengesteuerte Aktualisierung einer einzelnen Datenbank“ bezeichnet.

Die Idee der „gemeinsamen Transaktionen“ und die beiden hier aufgeführten Verarbeitungsmethoden sind in praktischen Anwendungen nicht befürwortungswürdig. Es gibt nur wenige erfolgreiche Fälle, in denen diese Methode verwendet wird. Fast alle Informationen, die abgefragt werden können, stammen aus dem Artikel „Verteilte Transaktionen“ von Spring im Frühjahr, mit und ohne XA“ von Kernentwickler Dave Syer. Um die Logik der Erzählung zu vervollständigen, listet der Autor in diesem Kapitel gemeinsam genutzte Transaktionen als eine der vier Arten von Transaktionen auf. Obwohl es in der Realität nicht ungewöhnlich ist, Datenbanken nach der Aufteilung von Mikrodiensten gemeinsam zu nutzen, ist der Autor persönlich nicht damit einverstanden, gemeinsam genutzte Transaktionen routinemäßig zu verwenden Lösungen, die es zu berücksichtigen gilt.

verteilte Transaktion

Die in diesem Kapitel erwähnte verteilte Transaktion bezieht sich speziell auf den Transaktionsverarbeitungsmechanismus, bei dem mehrere Dienste gleichzeitig auf mehrere Datenquellen zugreifen. Bitte beachten Sie den Unterschied zwischen ihr und der „verteilten Transaktion“ im DTP-Modell. Die „Verteilung“, auf die sich das DTP-Modell bezieht, bezieht sich auf die Datenquelle und umfasst keine Dienste. Dieser Teil wurde im Abschnitt „Globale Transaktion“ besprochen. Der Begriff „verteilt“, auf den in diesem Abschnitt Bezug genommen wird, bezieht sich auf den Dienst. Genau genommen sollte er als „Transaktionsverarbeitungsmechanismus in der verteilten Dienstumgebung“ bezeichnet werden.

Vor dem Jahr 2000 hatte man gehofft, dass der XA-Transaktionsmechanismus in der in diesem Abschnitt erwähnten verteilten Umgebung gut angewendet werden könnte, aber dieser gute Wunsch wurde heute durch die CAP-Theorie völlig zunichte gemacht, und der nächste Schritt besteht darin, mit dem Widerspruch zwischen zu beginnen CAP und ACID.

Widerspruch zwischen CAP und ACID

Das CAP-Theorem (Consistency, Availability, Partition Tolerance Theorem), auch als Brewer-Theorem bekannt, entstand im Juli 2000 und wurde von Professor Eric Brewer von der University of California, Berkeley, auf dem „ACM Distributed Computing Principles Symposium (PODC)“ vorgeschlagen. eine Vermutung.
Fügen Sie hier eine Bildbeschreibung ein
Im Jahr 2022 bewiesen dann Seth Gilbert und Nancy Lynch vom Massachusetts Institute of Technology die CAP-Vermutung mit strengen mathematischen Überlegungen. Seitdem hat sich CAP offiziell von einer Vermutung zu einem bekannten und im Bereich des verteilten Rechnens anerkannten Theorem gewandelt. Dieser Satz beschreibt, dass in einem verteilten System bei Problemen mit gemeinsam genutzten Daten die folgenden drei Merkmale höchstens zwei davon gleichzeitig erfüllen können:

  • Konsistenz : Dies bedeutet, dass das, was die Daten zu jedem Zeitpunkt in einem verteilten Knoten sehen, den Erwartungen entspricht. Konsistenz hat in der verteilten Forschung eine ernstzunehmende Definition und weist mehrere Arten von Unterteilungen auf. Wenn wir in Zukunft verteilte Konsensalgorithmen diskutieren, werden wir die Konsistenz erneut erwähnen. Die Konsistenz, die auf die Kopierreplikation ausgerichtet ist, ist dieselbe wie die, die sich hier auf Datenbanken konzentriert. Streng genommen Die Konsistenz des Zustands ist nicht genau dieselbe. Wir werden die spezifischen Unterschiede im folgenden verteilten Konsensalgorithmus diskutieren.
  • Verfügbarkeit : Stellt die Fähigkeit des Systems dar, ununterbrochene Dienste bereitzustellen. Um die Verfügbarkeit zu verstehen, müssen Sie zunächst zwei Indikatoren verstehen, die eng damit verbunden sind: Zuverlässigkeit (Zuverlässigkeit) und Wartbarkeit (Wartbarkeit). Die Zuverlässigkeit wird anhand der Mean Time Between Failure (MTBF) gemessen; die Wartbarkeit wird anhand der Mean Time To Repair (MTTR) gemessen. Die Verfügbarkeit misst das Verhältnis der Zeit, in der das System normal genutzt werden kann, zur Gesamtzeit, die wie folgt charakterisiert ist: A=MTBF/(MTBF+MTTR), d. h. Verfügbarkeit ist ein Verhältnis, das aus Zuverlässigkeit und Wartbarkeit berechnet wird, z. B. 99,9999 % verfügbar. Dies entspricht einer durchschnittlichen jährlichen Ausfallwiederherstellungszeit von 32 Sekunden.
  • Partitionstoleranz (Partitionstoleranz) : Stellt die Fähigkeit des Systems dar, Dienste ordnungsgemäß bereitzustellen, nachdem einige Knoten in einer verteilten Umgebung aus Netzwerkgründen den Kontakt zueinander verloren haben, dh wenn mit anderen Knoten eine „Netzwerkpartition“ gebildet wird.

CAP ist lediglich eine Auflistung von Konzepten und relativ abstrakt. Der Autor verwendet dennoch die am Anfang dieses Kapitels aufgeführten Szenariobeispiele, um zu veranschaulichen, was diese drei Funktionen für verteilte Systeme bedeuten werden. Angenommen, die Servicetopologie des Fenix-Buchladens ist in der folgenden Abbildung dargestellt. Auf eine Transaktionsanforderung eines Endbenutzers wird von einem Knoten im Konto-, Händler- und Lagerservice-Cluster geantwortet: In diesem System ist jeder einzelne Serviceknoten
Fügen Sie hier eine Bildbeschreibung ein
vorhanden seine eigene Datenbank (hier ist eine Annahme zur Vereinfachung der Erklärung des Problems. Im tatsächlichen Produktionssystem sollte es im Allgemeinen vermieden werden, Daten wie Benutzersalden so zu entwerfen, dass sie in mehreren beschreibbaren Datenbanken gespeichert werden), vorausgesetzt, dass eine Transaktionsanforderung gesendet wird von „ Kontoknoten 1“, „Händlerknoten 2“ und „Lagerknoten N“ antworten gemeinsam. Wenn ein Benutzer ein Produkt im Wert von 100 Yuan kauft, sollte Kontoknoten 1 zunächst 100 Yuan vom Konto des Benutzers abziehen. Es ist einfach, 100 Yuan in seiner eigenen Datenbank abzubuchen, aber er muss auch den Cluster über diese Transaktionsänderung Knoten 2 informieren zum Knoten N und stellen Sie sicher, dass die zugehörigen Daten im Händler- und anderen Kontoknoten des Lagerclusters korrekt geändert werden können. Zu diesem Zeitpunkt werden die folgenden möglichen Situationen auftreten.

  • Wenn die Änderungsinformationen nicht rechtzeitig mit anderen Kontoknoten synchronisiert werden, kann es vorkommen, dass der Benutzer beim Kauf eines anderen Produkts dieses einem anderen Knoten zur Verarbeitung zuweist. Aufgrund des falschen Kontostands liegt ein Fehler vor, der nicht vorliegen konnte Bei versehentlichen Transaktionen handelt es sich um ein Konsistenzproblem .
  • Wenn die Änderungsinformationen mit anderen Kontoknoten synchronisiert werden müssen, muss der Transaktionsdienst für den Benutzer vorübergehend gestoppt werden, bis die Daten synchronisiert sind, und dann wieder aufgenommen werden, was dazu führen kann, dass der Benutzer aufgrund der vorübergehenden Unfähigkeit, Dienste bereitzustellen, blockiert wird Der Benutzer kauft das nächste Mal. Die Transaktion wurde abgelehnt. Dies ist ein Verfügbarkeitsproblem .
  • Wenn einige Knoten im Kontodienstcluster aufgrund von Netzwerkproblemen normalerweise keine Kontoänderungsinformationen mit einem anderen Teil der Knoten austauschen können, ist der von einem Teil der Knoten im Dienstcluster bereitgestellte Dienst möglicherweise falsch. Kann die Partitionstoleranz .

Das Obige bezieht sich nur auf das CAP-Problem des Kontodienst-Clusters selbst. Für die gesamte Fenix's Bookstore-Site gibt es CAP-Probleme, die durch den Konto-, Händler- und Lagerdienst-Cluster verursacht werden. Beispielsweise wird nach dem Abzug des Benutzerkontos aufgrund von Alle Knoten im Lagerdienst wurden nicht rechtzeitig benachrichtigt, was aufgrund falscher Bestandsdaten im Lager, die in einer anderen Transaktion angezeigt wurden, zu einem Überverkauf führte. Ein weiteres Beispiel ist, dass der Transaktionsdienst der Ware vorübergehend gesperrt ist, weil eine Transaktion mit einer bestimmten Ware im Lager im Gange ist, um die Transaktionsänderungen von Benutzern, Händlern und Lagern zu synchronisieren, was zu Problemen bei der Benutzerfreundlichkeit usw. führt .

Da das CAP-Theorem rigoros bewiesen wurde, wird in diesem Abschnitt nicht erörtert, warum CAP nicht beides haben kann, sondern es werden direkt die unterschiedlichen Auswirkungen analysiert, wenn C, A und P verworfen werden.

Wenn die Partitionstoleranz wegfällt (CA ohne P)

Das heißt, wir gehen davon aus, dass die Kommunikation zwischen Knoten immer zuverlässig ist. In einem verteilten System darf keine immer zuverlässige Kommunikation aufgebaut werden. Das ist keine Frage, ob man darüber nachdenkt, aber solange das Netzwerk zum Teilen von Daten genutzt wird, wird es immer Partitionen geben. In Wirklichkeit ist das einfachste Beispiel für die Aufgabe der Partitionstoleranz der traditionelle relationale Datenbankcluster. Obwohl ein solcher Cluster immer noch mehrere über das Netzwerk verbundene Knoten für die Zusammenarbeit verwendet, werden die Daten nicht über das Netzwerk gemeinsam genutzt. Am Beispiel des RAC-Clusters von Oracle verfügt jeder Knoten über ein eigenes unabhängiges SGA, Redo-Protokoll, Rollback-Protokoll und andere Komponenten, aber jeder Knoten erhält Daten über dieselbe Datendatei und Steuerdatei im gemeinsam genutzten Speicher, um Netzwerkpartitionen durch die gemeinsame Nutzung von Festplatten zu vermeiden . Obwohl Oracle RAC ebenfalls eine aus mehreren Instanzen bestehende Datenbank ist, kann es daher nicht als verteilte Datenbank bezeichnet werden.
Fügen Sie hier eine Bildbeschreibung ein

Bei Verzicht auf Verfügbarkeit (CP ohne A)

Dies bedeutet, dass wir davon ausgehen, dass die Informationssynchronisationszeit zwischen Knoten nach der Partitionierung des Netzwerks auf unbestimmte Zeit verlängert werden kann. Zu diesem Zeitpunkt entspricht das Problem einer Entartung in das Szenario, in dem ein System mehrere Datenquellen verwendet, die im vorherigen Abschnitt „Global“ erläutert wurden „Transaktion“ können wir durch 2PC/3PC und andere Mittel gleichzeitig Partitionstoleranz und Konsistenz erreichen. In der Realität wird das CP-System, das sich dafür entscheidet, die Verfügbarkeit aufzugeben, im Allgemeinen in Fällen verwendet, in denen eine hohe Datenqualität erforderlich ist. Zusätzlich zu den verteilten Datenbanktransaktionen des DTP-Modells gehört auch die bekannte HBase zum CP-System. Nehmen Sie die HBase Cluster als Beispiel: Wenn ein bestimmter RegionServer ausfällt, sind alle von diesem RegionServer gehaltenen Schlüsselwertbereiche offline, bis der Datenwiederherstellungsprozess abgeschlossen ist. Die für diesen Prozess benötigte Zeit kann nicht im Voraus geschätzt werden.
Fügen Sie hier eine Bildbeschreibung ein

Wenn Sie die Konsistenz aufgeben (AP ohne C)

Das heißt, wir gehen davon aus, dass die zwischen den Knoten bereitgestellten Daten nach einer Partitionierung möglicherweise inkonsistent sind. Das AP-System, das die Konsistenz aufgibt, ist derzeit die gängige Wahl für den Entwurf verteilter Systeme, da P ein natürliches Attribut eines verteilten Netzwerks ist und man es nicht verwerfen kann, wenn man es nicht möchte; und A normalerweise das ist Zweck des Aufbaus eines verteilten Systems. Wenn die Anzahl zunimmt, aber abnimmt, verlieren viele verteilte Systeme möglicherweise ihren Existenzwert. Sofern Dienste im Zusammenhang mit Geldtransaktionen wie Bank- und Wertpapiergeschäfte nicht lieber unterbrochen werden, als Fehler zu machen, können die meisten Systeme mehr Knoten und eine geringere Verfügbarkeit nicht tolerieren . Derzeit sind die meisten NoSQL-Bibliotheken und verteilten Cache-Frameworks AP-Systeme. Wenn ein Redis-Knoten beispielsweise über eine Netzwerkpartition verfügt, verhindert dies immer noch nicht, dass jeder Knoten externe Cache-Dienste mit seinen eigenen lokal gespeicherten Daten bereitstellt Zu diesem Zeitpunkt ist es möglich, dass inkonsistente Daten an den Client zurückgegeben werden, wenn die Anforderung verschiedenen Knoten zugewiesen wird.

Nachdem ich dies gelesen habe, frage ich mich, ob Sie angesichts der Schlussfolgerung, dass „das AP-System, das die Konsistenz aufgibt, derzeit die gängige Wahl für den Entwurf verteilter Systeme ist“, ein wenig hilflos ist. Der ursprüngliche Zweck des Themas „Transaktion“ wird in diesem Kapitel besprochen besteht darin, „Konsistenz“ zu erreichen. In einer verteilten Umgebung muss „Konsistenz“ zu dem Attribut werden, das normalerweise geopfert und aufgegeben wird. Wenn wir jedoch ein Informationssystem aufbauen, müssen wir auf jeden Fall sicherstellen, dass die Betriebsergebnisse zumindest dann korrekt sind, wenn sie endgültig geliefert werden. Dieser Satz bedeutet, dass Datenfehler (Inkonsistenzen) im mittleren Prozess zulässig sind, aber sein sollten zum Zeitpunkt der Ausgabe korrigiert. Aus diesem Grund haben die Leute die Konsistenz neu definiert und die Konsistenz, die wir in CAP und ACID besprochen haben, als „starke Konsistenz“ bezeichnet, manchmal auch als „Linearisierbarkeit“ (normalerweise in der Diskussionsszene des Konsensalgorithmus). Das Verhalten, das AP-System von C zu opfern und so weit wie möglich das richtige Ergebnis zu erzielen, wird als Streben nach „schwacher Konsistenz“ bezeichnet. Wenn Sie jedoch einfach „schwache Konsistenz“ sagen, bedeutet dies tatsächlich „keine Garantie für Konsistenz“ ... Die menschliche Sprache ist wirklich breit und tiefgründig. In der schwachen Konsistenz haben die Leute einen etwas stärkeren Sonderfall namens „Eventual Consistency“ zusammengefasst, was bedeutet: Wenn die Daten über einen bestimmten Zeitraum nicht durch einen anderen Vorgang geändert wurden, wird schließlich das gleiche Ergebnis wie bei der starken Konsistenz erzielt Prozess. Manchmal wird der Algorithmus für die endgültige Konsistenz auch als „optimistischer Replikationsalgorithmus“ bezeichnet.

Bei dem in diesem Abschnitt behandelten Thema „verteilte Transaktionen“ muss das Ziel ebenfalls von der starken Konsistenz, die von den vorherigen drei Transaktionsmodi angestrebt wurde, auf das Streben nach „endgültiger Konsistenz“ reduziert werden. Aufgrund der Änderung der Konsistenzdefinition wurde auch die Bedeutung des Begriffs „Transaktion“ erweitert. Transaktionen, die ACID verwenden, werden von Menschen als „starre Transaktionen“ bezeichnet und beziehen sich kollektiv auf die gängige Praxis mehrerer verteilter Transaktionen, die der Autor beschreibt im Folgenden als „Flexible Transaktionen“ vorstellen.

zuverlässige Ereigniswarteschlange

Das Konzept der letztendlichen Konsistenz wurde vom Systemarchitekten von eBay, Dan Pritchett, in dem 2008 von ACM veröffentlichten Artikel „Base: An Acid Alternative“ vorgeschlagen. Dieser Artikel fasst eine starke, von ACID unabhängige Konsistenz und Möglichkeiten zur Verwendung von BASE für Konsistenzzwecke zusammen.

BASIS sind:

  • BA: Grundsätzlich verfügbar
  • S: Weicher Zustand
  • E: Letztendlich konsistent

Die Idee von BASE besteht darin, dem schlechten Geschmack von Datenbankwissenschaftlern, die gerne Abkürzungen erfinden, freien Lauf zu lassen. Mit dem eingängigen Begriff ACID vs. BASE (Säure vs. Base) verbreitet sich der Einfluss des Papiers jedoch schnell genug . Hier werde ich nicht viel über die konzeptionellen Probleme in BASE sprechen. Obwohl es eine schlechte Idee ist, es lächerlich zu machen, ist dieses Papier selbst der Ursprung des Konzepts der endgültigen Konsistenz und fasst systematisch ein technisches Mittel für verteilte Transaktionen zusammen. ist sehr wertvoll .

Wir verwenden weiterhin die Szenariobeispiele in diesem Kapitel, um den spezifischen Ansatz der von Dan Pritchett vorgeschlagenen „zuverlässigen Ereigniswarteschlange“ zu erläutern. Das Ziel besteht weiterhin darin, die Daten im Konto, im Lager und in den Händlerdiensten während des Transaktionsprozesses korrekt zu ändern. Die folgende Abbildung listet das Sequenzdiagramm des Änderungsprozesses auf.
Fügen Sie hier eine Bildbeschreibung ein

  1. Der Endbenutzer sendet eine Transaktionsanfrage an den Buchladen von Fenix: Kaufen Sie ein Exemplar von „In-Depth Understanding of Java Virtual Machine“ im Wert von 100 Yuan.
  2. Der Buchladen von Fenix ​​führt zunächst eine a priori-Bewertung der Fehlerwahrscheinlichkeit für die drei Vorgänge Benutzerkontoabzug, Händlerkontoeinzug und Bestandslieferung durch. Die Reihenfolge ihrer Vorgänge richtet sich nach der Größe der Fehlerwahrscheinlichkeit. Diese Bewertung spiegelt sich im Allgemeinen direkt wider Im Programmcode implementieren einige Großsysteme möglicherweise auch eine dynamische Sortierung. Laut Statistik besteht die wahrscheinlichste Transaktionsausnahme beispielsweise darin, dass der Benutzer das Produkt gekauft hat, dem Abzug der Zahlung jedoch nicht zugestimmt hat, oder dass der Kontostand nicht ausreicht; die zweite Ausnahme besteht darin, dass das Lager festgestellt hat, dass das Produkt nicht auf Lager ist ausreichend und konnte die Ware nicht liefern; das geringste Risiko war der Zahlungseingang, wenn es um die Abholung durch den Händler geht, gibt es in der Regel keine Überraschungen. Die Bestellung sollte so gestaltet werden, dass sie zuerst am fehleranfälligsten ist, also: Kontoabzug → Lagerlieferung → Händlerabholung.
  3. Der Kontodienst führt das Abzugsgeschäft durch. Wenn der Abzug erfolgreich ist, erstellt er eine Nachrichtentabelle in seiner eigenen Datenbank und speichert darin eine Nachricht: „Transaktions-ID: bestimmte UUID, Abzug: 100 Yuan (Status: abgeschlossen), Lager ausgegeben.“ Bibliothek „Vertiefendes Verständnis der Java Virtual Machine“: 1 Buch (Status: in Bearbeitung), Sammlung eines Händlers: 100 Yuan (Status: in Bearbeitung)“, beachten Sie, dass in diesem Schritt „Geschäft abbuchen“ und „Nachricht schreiben“ erforderlich ist. Verwenden Sie dieselbe lokale Transaktion, um in die eigene Datenbank des Kontodienstes zu schreiben.
  4. Erstellen Sie einen Nachrichtendienst im System, fragen Sie regelmäßig die Nachrichtentabelle ab und senden Sie die Nachricht mit dem Status „in Bearbeitung“ gleichzeitig an die Inventar- und Händlerdienstknoten (sie kann auch seriell gesendet werden, d. h. danach eine weitere senden). (man ist erfolgreich, aber in dem Szenario, das wir besprechen, ist es nicht notwendig). Zu diesem Zeitpunkt können die folgenden Situationen auftreten.
  • 1) Sowohl der Händler als auch der Lagerdienst haben die Abhol- und Lieferarbeiten erfolgreich abgeschlossen und das Ausführungsergebnis an den Benutzerkontoserver zurückgegeben, und der Benutzerkontodienst hat den Nachrichtenstatus von „in Bearbeitung“ auf „abgeschlossen“ aktualisiert. Die gesamte Transaktion erklärt ein erfolgreiches Ende und erreicht einen Zustand endgültiger Konsistenz.
  • 2) Mindestens einer der Händler oder Lagerdienste erhält aus Netzwerkgründen die Nachricht vom Benutzerkontodienst nicht. Da der auf dem Benutzerkontoserver gespeicherte Nachrichtenstatus zu diesem Zeitpunkt immer „in Bearbeitung“ lautet, sendet der Nachrichtenserver bei jeder Abfrage weiterhin wiederholt Nachrichten an nicht reagierende Dienste. Die Wiederholbarkeit dieses Schritts bestimmt, dass alle vom Nachrichtenserver gesendeten Nachrichten idempotent sein müssen. Das übliche Design besteht darin, die Nachricht mit einer eindeutigen Transaktions-ID zu versehen, um sicherzustellen, dass die ausgehenden und Zahlungsvorgänge in einer Transaktion nur einmal verarbeitet werden.
  • 3) Einige oder alle Händler oder Lagerdienste können die Arbeit nicht abschließen. Beispielsweise stellt das Lager fest, dass „Vertiefendes Verständnis der Java Virtual Machine“ nicht vorrätig ist. Zu diesem Zeitpunkt wird es automatisch fortgesetzt Senden Sie die Nachricht erneut, bis der Vorgang erfolgreich ist (z. B. neues Inventar auffüllen) oder bis manuell eingegriffen wird. Es ist ersichtlich, dass es, solange der erste Schritt der zuverlässigen Ereigniswarteschlange abgeschlossen ist, bei der Nachverfolgung kein Konzept für ein Fehler-Rollback gibt und nur Erfolg zulässig ist und kein Fehler zulässig ist.
  • 4) Der Händler und der Lagerdienst haben die Abhol- und Lieferarbeiten erfolgreich abgeschlossen, aber die Antwortnachricht geht aus Netzwerkgründen verloren. Zu diesem Zeitpunkt sendet der Benutzerkontodienst die nächste Nachricht noch erneut, aber da der Vorgang idempotent ist, Dies wird nicht der Fall sein Dies führt zu wiederholten Lieferungen und Abholungen und führt lediglich dazu, dass der Händler und der Lagerserver erneut eine Antwortnachricht senden. Dieser Vorgang wird wiederholt, bis die Netzwerkkommunikation zwischen den beiden Parteien wieder normal ist.

Es gibt auch einige Nachrichten-Frameworks, die verteilte Transaktionen unterstützen, wie beispielsweise RocketMQ, das verteilte Transaktionsvorgänge nativ unterstützt. Zu diesem Zeitpunkt können die oben genannten Situationen 2 und 4 auch durch das Nachrichten-Framework garantiert werden.

Die obige Lösung, die auf kontinuierlichen Wiederholungsversuchen beruht, um die Zuverlässigkeit sicherzustellen, ist nicht die erste oder ursprüngliche Erfindung von Dan Pritchett. Sie wurde häufig in anderen Bereichen der Datenverarbeitung verwendet und trägt einen speziellen Namen namens „Best Effort Delivery“. Beispielsweise gehört im TCP-Protokoll die Zuverlässigkeitsgarantie des automatischen erneuten Sendens von Paketen ohne Empfang einer ACK-Antwort zur Best-Effort-Zustellung. Es gibt auch eine häufigere Form einer zuverlässigen Ereigniswarteschlange, die als „Best-Effort 1PC“ bezeichnet wird und sich auf die Verwendung kontinuierlicher Wiederholungen bezieht, nachdem das wahrscheinlichste fehleranfällige Geschäft als lokale Transaktion abgeschlossen wurde das Nachrichtensystem), um den Abschluss anderer verwandter Geschäfte in derselben verteilten Transaktion zu fördern.

Supongo que te gusta

Origin blog.csdn.net/zkkzpp258/article/details/131360252
Recomendado
Clasificación