Warum benötigen Sie möglicherweise eine halbsynchrone Replikation?

Hallo zusammen. Vladislav Rodin ist in Kontakt. Derzeit unterrichte ich Kurse zu Software-Architektur und High-Stress-Software-Architektur an der OTUS. In Erwartung des Beginns eines neuen Kursstroms „Hochlastarchitekt“ Ich habe beschlossen, ein kurzes Stück Originalmaterial zu schreiben, das ich mit Ihnen teilen möchte.

Warum benötigen Sie möglicherweise eine halbsynchrone Replikation?

Einführung

Aufgrund der Tatsache, dass die Festplatte nur etwa 400–700 Vorgänge pro Sekunde ausführen kann (was mit typischen RPS auf einem Hochlastsystem nicht vergleichbar ist), ist die klassische Festplattendatenbank der Flaschenhals der Architektur. Daher muss den Skalierungsmustern dieses Speichers besondere Aufmerksamkeit gewidmet werden.

Derzeit gibt es zwei Datenbankskalierungsmuster: Replikation und Sharding. Mit Sharding können Sie den Schreibvorgang skalieren und dadurch die RPS pro Schreibvorgang pro Server in Ihrem Cluster reduzieren. Durch die Replikation können Sie dasselbe tun, jedoch mit Lesevorgängen. Diesem Muster ist dieser Artikel gewidmet.

Replikation

Wenn man die Replikation auf einer sehr hohen Ebene betrachtet, ist es eine einfache Sache: Man hatte einen Server, auf dem lagen Daten, und dann konnte dieser Server die Belastung durch das Lesen dieser Daten nicht mehr bewältigen. Sie fügen ein paar weitere Server hinzu, synchronisieren Daten auf allen Servern und der Benutzer kann von jedem Server in Ihrem Cluster lesen.

Trotz seiner scheinbaren Einfachheit gibt es mehrere Möglichkeiten, verschiedene Implementierungen dieses Schemas zu klassifizieren:

  • Nach Rollen im Cluster (Master-Master oder Master-Slave)
  • Nach gesendeten Objekten (zeilenbasiert, anweisungsbasiert oder gemischt)
  • Gemäß dem Knotensynchronisationsmechanismus

Heute beschäftigen wir uns mit Punkt 3.

Wie erfolgt ein Transaktions-Commit?

Dieses Thema steht nicht in direktem Zusammenhang mit der Replikation; es kann ein separater Artikel darüber geschrieben werden, aber da weitere Lektüre ohne Verständnis des Transaktions-Commit-Mechanismus nutzlos ist, möchte ich Sie an die grundlegendsten Dinge erinnern. Ein Transaktions-Commit erfolgt in drei Phasen:

  1. Protokollieren einer Transaktion im Datenbankprotokoll.
  2. Verwenden einer Transaktion in einer Datenbank-Engine.
  3. Rücksendung der Bestätigung an den Kunden, dass die Transaktion erfolgreich durchgeführt wurde.

In verschiedenen Datenbanken kann dieser Algorithmus Nuancen aufweisen: Beispielsweise gibt es in der InnoDB-Engine der MySQL-Datenbank zwei Protokolle: eines für die Replikation (Binärprotokoll) und das andere für die Aufrechterhaltung von ACID (Rückgängig-/Wiederholen-Protokoll), während in PostgreSQL Es gibt ein Protokoll, das beide Funktionen ausführt (Write-Ahead-Protokoll = WAL). Aber was oben dargestellt wird, ist genau das allgemeine Konzept, das es erlaubt, solche Nuancen nicht zu berücksichtigen.

Synchrone (sync) Replikation

Fügen wir Logik hinzu, um die empfangenen Änderungen am Transaktions-Commit-Algorithmus zu replizieren:

  1. Protokollieren einer Transaktion im Datenbankprotokoll.
  2. Verwenden einer Transaktion in einer Datenbank-Engine.
  3. Senden von Daten an alle Replikate.
  4. Empfangen einer Bestätigung von allen Replikaten, dass eine Transaktion auf ihnen abgeschlossen wurde.
  5. Rücksendung der Bestätigung an den Kunden, dass die Transaktion erfolgreich durchgeführt wurde.

Mit diesem Ansatz ergeben sich eine Reihe von Nachteilen:

  • Der Client wartet darauf, dass die Änderungen auf alle Replikate angewendet werden.
  • Wenn die Anzahl der Knoten im Cluster zunimmt, verringern wir die Wahrscheinlichkeit, dass der Schreibvorgang erfolgreich ist.

Wenn mit dem 1. Punkt alles einigermaßen klar ist, dann lohnt es sich, die Gründe für den 2. Punkt zu erläutern. Wenn wir während der synchronen Replikation keine Antwort von mindestens einem Knoten erhalten, setzen wir die Transaktion zurück. Wenn Sie also die Anzahl der Knoten im Cluster erhöhen, erhöhen Sie die Wahrscheinlichkeit, dass ein Schreibvorgang fehlschlägt.

Können wir auf die Bestätigung nur von einem bestimmten Prozentsatz der Knoten warten, beispielsweise von 51 % (Quorum)? Ja, das können wir, aber in der klassischen Version ist eine Bestätigung von allen Knoten erforderlich, denn so können wir eine vollständige Datenkonsistenz im Cluster gewährleisten, was zweifellos ein Vorteil dieser Art der Replikation ist.

Asynchrone (asynchrone) Replikation

Lassen Sie uns den vorherigen Algorithmus ändern. Wir werden „irgendwann später“ Daten an die Replikate senden und „irgendwann später“ werden die Änderungen auf die Replikate angewendet:

  1. Protokollieren einer Transaktion im Datenbankprotokoll.
  2. Verwenden einer Transaktion in einer Datenbank-Engine.
  3. Rücksendung der Bestätigung an den Kunden, dass die Transaktion erfolgreich durchgeführt wurde.
  4. Senden von Daten an Replikate und Anwenden von Änderungen auf diese.

Dieser Ansatz führt dazu, dass der Cluster schnell arbeitet, da wir den Client nicht darauf warten lassen, dass die Daten die Replikate erreichen und sogar festgeschrieben werden.

Aber die Bedingung, dass Daten „irgendwann später“ auf Replikate abgelegt werden, kann zum Verlust einer Transaktion und zum Verlust einer vom Benutzer bestätigten Transaktion führen, denn wenn die Daten keine Zeit zum Replizieren hatten, erfolgt eine Bestätigung an den Client Über den Erfolg der Operation wurde gesendet, und die Festplatte des Knotens, auf dem die Änderungen angekommen sind, ist abgestürzt. Wir verlieren die Transaktion, was zu sehr unangenehmen Folgen führen kann.

Semisync-Replikation

Schließlich kommen wir zur halbsynchronen Replikation. Diese Art der Replikation ist nicht sehr bekannt oder sehr verbreitet, aber sie ist von erheblichem Interesse, da sie die Vorteile sowohl der synchronen als auch der asynchronen Replikation kombinieren kann.

Versuchen wir, die beiden vorherigen Ansätze zu kombinieren. Wir werden den Client nicht lange behalten, aber wir werden verlangen, dass die Daten repliziert werden:

  1. Protokollieren einer Transaktion im Datenbankprotokoll.
  2. Verwenden einer Transaktion in einer Datenbank-Engine.
  3. Senden von Daten an Replikate.
  4. Erhalt einer Bestätigung vom Replikat, dass die Änderungen empfangen wurden (sie werden „irgendwann später“ angewendet).
  5. Rücksendung der Bestätigung an den Kunden, dass die Transaktion erfolgreich durchgeführt wurde.

Bitte beachten Sie, dass es bei diesem Algorithmus nur dann zu Transaktionsverlusten kommt, wenn sowohl der Knoten, der die Änderungen empfängt, als auch der Replikatknoten ausfallen. Die Wahrscheinlichkeit eines solchen Ausfalls wird als gering eingeschätzt und diese Risiken werden in Kauf genommen.

Bei diesem Ansatz besteht jedoch möglicherweise die Gefahr von Phantomlesungen. Stellen wir uns das folgende Szenario vor: In Schritt 4 haben wir von keinem Replikat eine Bestätigung erhalten. Wir müssen diese Transaktion rückgängig machen und dürfen keine Bestätigung an den Kunden zurücksenden. Da die Daten in Schritt 2 angewendet wurden, gibt es zwischen dem Ende von Schritt 2 und dem Rollback der Transaktion eine Zeitlücke, in der parallele Transaktionen Änderungen sehen können, die nicht in der Datenbank sein sollten.

Verlustfreie Semisync-Replikation

Wenn Sie ein wenig nachdenken, können Sie die Schritte des Algorithmus einfach umkehren und das Problem der Phantom-Lesevorgänge in diesem Szenario beheben:

  1. Protokollieren einer Transaktion im Datenbankprotokoll.
  2. Senden von Replikatdaten.
  3. Erhalt einer Bestätigung vom Replikat, dass die Änderungen empfangen wurden (sie werden „irgendwann später“ angewendet).
  4. Verwenden einer Transaktion in einer Datenbank-Engine.
  5. Rücksendung der Bestätigung an den Kunden, dass die Transaktion erfolgreich durchgeführt wurde.

Jetzt übernehmen wir Änderungen nur dann, wenn sie repliziert wurden.

Abschluss

Wie immer gibt es keine idealen Lösungen; es gibt eine Reihe von Lösungen, von denen jede ihre eigenen Vor- und Nachteile hat und zur Lösung unterschiedlicher Problemklassen geeignet ist. Dies gilt absolut für die Auswahl eines Mechanismus zum Synchronisieren von Daten in einer replizierten Datenbank. Die Vorteile der halbsynchronen Replikation sind so solide und interessant, dass sie trotz ihrer geringen Verbreitung als beachtenswert angesehen werden kann.

Das ist alles. Wir sehen uns um Kurs!

Source: habr.com

Kommentar hinzufügen