Grundlegendes zu active-active-Konflikten - Amazon Relational Database Service

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Grundlegendes zu active-active-Konflikten

Wenn Sie pgactive im active-active-Modus verwenden, kann das Schreiben in dieselben Tabellen über mehrere Knoten zu Datenkonflikten führen. Während einige Clustering-Systeme verteilte Sperren verwenden, um gleichzeitigen Zugriff zu verhindern, verfolgt pgactive einen optimistischen Ansatz, der sich besser für geografisch verteilte Anwendungen eignet.

Einige Clustering-Systeme von Datenbanken verhindern den gleichzeitigen Datenzugriff mithilfe von verteilten Sperren. Dieser Ansatz funktioniert zwar, wenn sich Server in unmittelbarer Nähe befinden, unterstützt jedoch keine geografisch verteilten Anwendungen, da er für eine gute Leistung eine extrem geringe Latenz erfordert. Anstelle von verteilten Sperren (ein pessimistischer Ansatz) verwendet die pgactive-Erweiterung einen optimistischen Ansatz. Das heißt:

  • Sie hilft Ihnen, Konflikte nach Möglichkeit zu vermeiden.

  • Sie lässt das Auftreten bestimmter Arten von Konflikten zu.

  • Sie stellt Konfliktlösungen bereit, wenn Konflikte auftreten.

Dieser Ansatz bietet Ihnen mehr Flexibilität beim Erstellen verteilter Anwendungen.

So entstehen Konflikte

Konflikte zwischen Knoten entstehen durch Abfolgen von Ereignissen, die nicht auftreten könnten, wenn alle beteiligten Transaktionen gleichzeitig auf demselben Knoten stattfinden würden. Da die Knoten Änderungen erst nach dem Festschreiben von Transaktionen austauschen, ist jede Transaktion einzeln auf dem Knoten gültig, auf dem sie festgeschrieben wurde. Sie wäre jedoch nicht gültig, wenn sie auf einem anderen Knoten ausgeführt würde, der in der Zwischenzeit andere Aufgaben durchgeführt hat. Da „pgactive apply“ die Transaktion im Wesentlichen auf den anderen Knoten wiedergibt, kann der Wiedergabevorgang fehlschlagen, wenn ein Konflikt zwischen einer angewendeten Transaktion und einer Transaktion besteht, die auf dem Empfängerknoten festgeschrieben wurde.

Der Grund dafür, dass die meisten Konflikte nicht auftreten können, wenn alle Transaktionen auf einem einzigen Knoten ausgeführt werden, besteht darin, dass PostgreSQL über Kommunikationsmechanismen zwischen Transaktionen verfügt, um dies zu verhindern. Hierzu zählen die folgenden:

  • UNIQUE-Indizes

  • SEQUENCE-Vorgänge

  • Sperren von Zeilen und Beziehungen

  • Nachverfolgung von SERIALIZABLE-Abhängigkeiten

All diese Mechanismen sind Möglichkeiten der Kommunikation zwischen Transaktionen zur Vermeidung unerwünschter Parallelitätsprobleme.

pgactive erreicht eine niedrige Latenz und verarbeitet Netzwerkpartitionen gut, da es keinen verteilten Transaktionsmanager oder Sperrmanager verwendet. Dies bedeutet jedoch, dass Transaktionen auf verschiedenen Knoten völlig isoliert voneinander ausgeführt werden. Während die Isolierung in der Regel zu einer Verbesserung der Datenbankkonsistenz beiträgt, müssen Sie sie in diesem Fall reduzieren, um Konflikte zu vermeiden.

Typen von Konflikten

Zu den möglichen Konflikten gehören:

PRIMARY KEY- oder UNIQUE-Konflikte

Zeilenkonflikte treten auf, wenn mehrere Vorgänge versuchen, denselben Zeilenschlüssel auf eine Weise zu ändern, die auf einem einzelnen Knoten nicht möglich ist. Diese Konflikte stellen den häufigsten Typ von Datenkonflikten dar.

pgactive löst erkannte Konflikte mithilfe der „last-update-wins“-Behandlung oder des benutzerdefinierten Konflikt-Handlers.

Zu den Zeilenkonflikten gehören:

  • INSERT vs. INSERT

  • INSERT vs. UPDATE

  • UPDATE vs. DELETE

  • INSERT vs. DELETE

  • DELETE vs. DELETE

  • INSERT vs. DELETE

INSERT/INSERT-Konflikte

Dieser häufigste Konflikt tritt auf, wenn INSERTs auf zwei verschiedenen Knoten ein Tupel mit denselben PRIMARY KEY-Werten (oder identischen UNIQUE-Einschränkungswerten, wenn kein PRIMARY KEY vorhanden ist) erzeugen.

pgactivelink löst INSERT-Konflikte, indem es den Zeitstempel des ursprünglichen Hosts verwendet, um das neueste Tupel beizubehalten. Sie können dieses Standardverhalten mit dem benutzerdefinierten Konflikt-Handler außer Kraft setzen. Dieser Prozess erfordert zwar keine spezielle Administratoraktion, beachten Sie jedoch, dass pgactivelink einen der INSERT-Vorgänge auf allen Knoten verwirft. Es findet keine automatische Datenzusammenführung statt, es sei denn, der benutzerdefinierte Handler implementiert sie.

pgactivelink kann nur Konflikte lösen, die eine einzelne Einschränkungsverletzung betreffen. Wenn ein INSERT-Vorgang gegen mehrere UNIQUE-Einschränkungen verstößt, müssen Sie zusätzliche Strategien zur Konfliktlösung implementieren.

INSERT-Vorgänge, die gegen mehrere UNIQUE-Einschränkungen verstoßen

Ein INSERT/INSERT-Konflikt kann mehrere UNIQUE-Einschränkungen verletzen, einschließlich des PRIMARY KEY. pgactivelink kann nur Konflikte behandeln, die eine einzelne UNIQUE-Einschränkung beinhalten. Wenn Konflikte mehrere UNIQUE-Einschränkungen verletzen, schlägt der Apply-Worker fehl und gibt den folgenden Fehler zurück:

multiple unique constraints violated by remotely INSERTed tuple.

In älteren Versionen führte diese Situation stattdessen zu dem Fehler „diverging uniqueness conflict“.

Um diese Konflikte zu lösen, müssen Sie manuelle Maßnahmen ergreifen. Führen Sie für die widersprüchlichen lokalen Tupel entweder einen DELETE- oder einen UPDATE-Vorgang durch, um Konflikte mit dem neuen Remote-Tupel zu beseitigen. Beachten Sie, dass Sie möglicherweise mehrere in Konflikt stehende Tupel beheben müssen. Derzeit stellt pgactivelink keine integrierten Funktionen zum Ignorieren, Verwerfen oder Zusammenführen von Tupeln bereit, die gegen mehrere eindeutige Einschränkungen verstoßen.

Anmerkung

Weitere Informationen finden Sie unter „UPDATE-Vorgänge, die gegen mehrere UNIQUE-Einschränkungen verstoßen“.

UPDATE/UPDATE-Konflikte

Dieser Konflikt tritt auf, wenn zwei Knoten gleichzeitig dasselbe Tupel ändern, ohne dessen PRIMARY KEY zu ändern. pgactivelink löst diese Konflikte mithilfe der „last-update-wins“-Logik oder des benutzerdefinierten Konflikt-Handlers, falls definiert. Ein PRIMARY KEY ist für den Tupel-Abgleich und die Konfliktlösung unerlässlich. Für Tabellen ohne PRIMARY KEY lehnt pgactivelink UPDATE-Vorgänge mit dem folgenden Fehler ab:

Cannot run UPDATE or DELETE on table (tablename) because it does not have a primary key.

UPDATE-Konflikte beim PRIMARY KEY

Für pgactive gelten Einschränkungen beim Verarbeiten von PRIMARY KEY-Aktualisierungen. Sie können zwar einen UPDATE-Vorgang für einen PRIMARY KEY ausführen, pgactive kann Konflikte jedoch nicht automatisch mithilfe der „last-update-wins“-Logik für diese Vorgänge lösen. Sie müssen sicherstellen, dass die PRIMARY KEY-Aktualisierungen nicht mit vorhandenen Werten in Konflikt stehen. Wenn während der PRIMARY KEY-Aktualisierungen Konflikte auftreten, werden diese zu divergierenden Konflikten, die Ihr manuelles Eingreifen erfordern. Weitere Informationen zum Umgang mit diesen Situationen finden Sie unter Divergierende Konflikte.

UPDATE-Vorgänge, die gegen mehrere UNIQUE-Einschränkungen verstoßen

pgactivelink kann die Lösung von „last-update-wins“-Konflikten nicht anwenden, wenn ein eingehender UPDATE-Vorgang gegen mehrere UNIQUE-Einschränkungen oder PRIMARY KEY-Werte verstößt. Dieses Verhalten entspricht INSERT-Vorgängen mit mehreren Einschränkungsverstößen. Diese Situationen führen zu divergierenden Konflikten, die Ihr manuelles Eingreifen erfordern. Weitere Informationen finden Sie unter Divergierende Konflikte.

UPDATE/DELETE-Konflikte

Diese Konflikte treten auf, wenn ein Knoten für eine Zeile UPDATE-Vorgänge und ein anderer gleichzeitig DELETE-Vorgänge für sie durchführt. In diesem Fall tritt bei der Wiederholung ein UPDATE/DELETE-Konflikt auf. Die Lösung besteht darin, alle nach einem DELETE-Vorgang eingehenden UPDATE-Vorgänge zu verwerfen, sofern der benutzerdefinierte Konflikt-Handler nichts anderes angibt.

pgactivelink erfordert einen PRIMARY KEY, um Tupel abzugleichen und Konflikte zu lösen. Für Tabellen ohne PRIMARY KEY lehnt pgactivelink UPDATE-Vorgänge mit dem folgenden Fehler ab:

Cannot run UPDATE or DELETE on table (tablename) because it does not have a primary key.

Anmerkung

pgactivelink kann nicht zwischen UPDATE/DELETE- und INSERT/UPDATE-Konflikten unterscheiden. In beiden Fällen wirkt sich ein UPDATE-Vorgang auf eine nicht vorhandene Zeile aus. Aufgrund der asynchronen Replikation und der fehlenden Reihenfolge der Wiederholung zwischen den Knoten kann pgactivelink nicht feststellen, ob der UPDATE-Vorgang für eine neue Zeile (INSERT noch nicht empfangen) oder für eine gelöschte Zeile gilt. In beiden Szenarien wird der UPDATE-Vorgang von pgactivelink verworfen.

INSERT/UPDATE-Konflikte

Dieser Konflikt kann in Umgebungen mit mehreren Knoten auftreten. Dies ist dann der Fall, wenn ein Knoten einen INSERT-Vorgang und ein zweiter Knoten einen UPDATE-Vorgang für eine Zeile ausführt und an einen dritten Knoten ein UPDATE-Vorgang vor dem ursprünglichen INSERT-Vorgang übermittelt wird. Standardmäßig löst pgactivelink diese Konflikte, indem es den UPDATE-Vorgang verwirft, sofern der benutzerdefinierte Konflikt-Trigger nichts anderes angibt. Beachten Sie, dass diese Lösungsmethode zu Dateninkonsistenzen zwischen den Knoten führen kann. Weitere Informationen zu ähnlichen Szenarien und deren Handhabung finden Sie unter UPDATE/DELETE-Konflikte.

DELETE/DELETE-Konflikte

Dieser Konflikt tritt auf, wenn zwei verschiedene Knoten gleichzeitig dasselbe Tupel löschen. pgactivelink betrachtet diese Konflikte als harmlos, da das Endergebnis beider DELETE-Vorgänge identisch ist. In diesem Szenario ignoriert pgactivelink sicher einen der DELETE-Vorgänge, ohne die Datenkonsistenz zu beeinträchtigen.

Konflikte bei Fremdschlüsseleinschränkungen

FOREIGN KEY-Einschränkungen können zu Konflikten führen, wenn Remote-Transaktionen auf vorhandene lokale Daten angewendet werden. Diese Konflikte treten in der Regel auf, wenn Transaktionen in einer anderen Reihenfolge als in ihrer logischen Reihenfolge auf die Ursprungsknoten angewendet werden.

Standardmäßig wendet pgactive Änderungen mit session_replication_role als replica an, wodurch Fremdschlüsselprüfungen während der Replikation umgangen werden. In active-active-Konfigurationen kann dies zu Verstößen bei Fremdschlüsseln führen. Die meisten Verstöße sind vorübergehend und werden behoben, sobald die Replikation abgeschlossen ist. Es können jedoch hängende Fremdschlüssel auftreten, da pgactive das knotenübergreifende Sperren von Zeilen nicht unterstützt.

Dieses Verhalten ist partitionstoleranten asynchronen active-active-Systemen inhärent. Beispielsweise kann Knoten A eine neue untergeordnete Zeile einfügen und Knoten B gleichzeitig seine übergeordnete Zeile löschen. Das System kann diese Art der gleichzeitigen knotenübergreifenden Änderung nicht verhindern.

Zur Minimierung von Fremdschlüsselkonflikten wird Folgendes empfohlen:

  • Beschränken Sie Fremdschlüsselbeziehungen auf eng verwandte Entitäten.

  • Ändern Sie verwandte Entitäten, wenn möglich, über einen einzigen Knoten.

  • Wählen Sie Entitäten aus, die selten geändert werden müssen.

  • Implementieren Sie die Parallelitätssteuerung auf Anwendungsebene für Änderungen.

Konflikte bei Ausschlusseinschränkungen

pgactivelink unterstützt keine Ausschlusseinschränkungen und beschränkt deren Erstellung.

Anmerkung

Wenn Sie eine vorhandene eigenständige Datenbank in eine pgactivelink-Datenbank konvertieren, löschen Sie manuell alle Ausschlussbeschränkungen.

In einem verteilten asynchronen System lässt sich nicht gewährleisten, dass kein Satz von Zeilen gegen die Einschränkung verstößt. Dies liegt daran, dass alle Transaktionen auf verschiedenen Knoten vollständig isoliert sind. Ausschlusseinschränkungen können zu Deadlocks bei der Wiederholung führen, sodass die Wiederholung aufgrund von Verstößen gegen Ausschlusseinschränkungen nicht von einem Knoten zum anderen fortgesetzt werden kann.

Wenn Sie pgactivelink zwingen, eine Ausschlusseinschränkung zu erstellen, oder wenn Sie beim Konvertieren einer eigenständigen Datenbank in pgactivelink vorhandene Einschränkungen nicht löschen, führt dies möglicherweise zu einer Unterbrechung der Replikation. Um den Replikationsfortschritt wiederherzustellen, entfernen oder ändern Sie die lokalen Tupel, die mit einem eingehenden Remote-Tupel in Konflikt stehen, sodass die Remote-Transaktion angewendet werden kann.

Konflikte bei globalen Daten

Bei der Verwendung von pgactivelink können Konflikte auftreten, wenn Knoten unterschiedliche globale systemweite PostgreSQL-Daten wie beispielsweise Rollen aufweisen. Diese Konflikte können dazu führen, dass Vorgänge – in erster Linie DDL – auf einem Knoten erfolgreich ausgeführt und festgeschrieben, jedoch nicht auf andere Knoten angewendet werden.

Wenn ein Benutzer auf einem Knoten vorhanden ist, auf einem anderen jedoch nicht, können Replikationsprobleme auftreten:

  • Node1 verfügt über einen Benutzer namens fred, der jedoch nicht auf Node2 vorhanden ist.

  • Wenn fred eine Tabelle auf Node1 erstellt, wird die Tabelle mit fred als Eigentümer repliziert.

  • Wenn dieser DDL-Befehl auf Node2 angewendet wird, schlägt er fehl, da der Benutzer fred nicht vorhanden ist.

  • Dieser Fehler generiert einen ERROR in den PostgreSQL-Protokollen auf Node2 und erhöht den Zähler pgactive.pgactive_stats.nr_rollbacks.

Lösung: Erstellen Sie den Benutzer fred auf Node2. Der Benutzer benötigt keine identischen Berechtigungen, sondern muss auf beiden Knoten vorhanden sein.

Wenn eine Tabelle auf einem Knoten vorhanden ist, auf einem anderen jedoch nicht, schlagen Datenänderungsvorgänge fehl:

  • Node1 verfügt über eine Tabelle mit dem Namen foo, die auf Node2 nicht vorhanden ist.

  • Alle DML-Vorgänge in der Tabelle foo auf Node1 schlagen fehl, wenn sie auf Node2 repliziert werden.

Lösung: Erstellen Sie die Tabelle foo auf Node2 mit derselben Struktur.

Anmerkung

pgactivelink repliziert derzeit keine CREATE USER-Befehle oder DDL-Vorgänge. Die DDL-Replikation ist für eine zukünftige Version geplant.

Sperrkonflikte und Deadlock-Abbrüche

Da „pgactive apply“-Prozesse wie normale Benutzersitzungen funktionieren, folgen sie den Standardregeln für das Sperren von Zeilen und Tabellen. Dies kann dazu führen, dass „pgactive apply“-Prozesse auf Sperren warten, die von Benutzertransaktionen oder anderen apply-Prozessen gehalten werden.

Die folgenden Typen von Sperren können sich auf apply-Prozesse auswirken:

  • Explizites Sperren auf Tabellenebene (LOCK TABLE...) durch Benutzersitzungen

  • Explizites Sperren auf Zeilenebene (SELECT... FOR UPDATE/FOR SHARE) nach Benutzersitzungen

  • Sperren mit Fremdschlüsseln

  • Implizite Sperrung aufgrund von UPDATE-, INSERT- oder DELETE-Vorgängen für Zeilen, entweder durch lokale Aktivitäten oder durch Anwendung über andere Server

Deadlocks können auftreten zwischen

  • einem „pgactivelink apply“-Prozess und einer Benutzertransaktion,

  • zwei apply-Prozessen.

Wenn Deadlocks auftreten, beendet Deadlock Detector von PostgreSQL eine der problematischen Transaktionen. Wenn der Prozess des „pgactivelink apply“-Workers beendet wird, versucht er es automatisch erneut, was in der Regel erfolgreich ist.

Anmerkung
  • Diese Probleme sind vorübergehend und erfordern im Allgemeinen kein Eingreifen des Administrators. Wenn ein apply-Prozess durch die Sperre einer inaktiven Benutzersitzung für einen längeren Zeitraum blockiert wird, können Sie die Benutzersitzung beenden, um die Replikation fortzusetzen. Diese Situation ähnelt einer Situation, in der ein Benutzer eine lange Sperre hält, die sich auf eine andere Benutzersitzung auswirkt.

  • Um Verzögerungen bei der Wiederholung im Zusammenhang mit Sperren zu identifizieren, aktivieren Sie die Funktion log_lock_waits in PostgreSQL.

Divergierende Konflikte

Divergierende Konflikte treten auf, wenn Daten, die knotenübergreifend identisch sein sollten, unerwartet voneinander abweichen. Obwohl diese Konflikte nicht auftreten sollten, können Sie in der aktuellen Implementierung nicht alle zuverlässig verhindert werden.

Anmerkung

Das Ändern des PRIMARY KEY einer Zeile kann zu unterschiedlichen Konflikten führen, wenn ein anderer Knoten den Schlüssel derselben Zeile ändert, bevor alle Knoten die Änderung verarbeiten. Vermeiden Sie es, Primärschlüssel zu ändern, oder beschränken Sie Änderungen auf einen bestimmten Knoten. Weitere Informationen finden Sie unter UPDATE-Konflikte beim PRIMARY KEY .

Divergierende Konflikte, die Zeilendaten betreffen, erfordern in der Regel ein Eingreifen des Administrators. Um diese Konflikte zu lösen, müssen Sie die Daten auf einem Knoten manuell so anpassen, dass sie mit denen eines anderen Knotens übereinstimmen, und gleichzeitig die Replikation mit pgactive.pgactive_do_not_replicate vorübergehend deaktivieren. Diese Konflikte sollten nicht auftreten, wenn Sie pgactive wie dokumentiert verwenden und Einstellungen oder Funktionen vermeiden, die als unsicher markiert sind.

Als Administrator müssen Sie diese Konflikte manuell lösen. Je nach Konflikttyp ist die Verwendung erweiterter Optionen wie pgactive.pgactive_do_not_replicate erforderlich. Nutzen Sie diese Optionen mit Vorsicht, da sich die Situation durch eine unsachgemäße Verwendung verschlechtern kann. Aufgrund der Vielzahl möglicher Konflikte können wir keine allgemeingültigen Lösungsansätze bereitstellen.

Divergierende Konflikte treten auf, wenn Daten, die auf verschiedenen Knoten identisch sein sollten, unerwartet voneinander abweichen. Obwohl diese Konflikte nicht auftreten sollten, können Sie in der aktuellen Implementierung nicht alle zuverlässig verhindert werden.

Vermeiden oder Tolerieren von Konflikten

In den meisten Fällen können Sie ein geeignetes Anwendungsdesign verwenden, um Konflikte zu vermeiden oder die Anwendung konflikttolerant zu gestalten.

Konflikte treten nur auf, wenn Vorgänge auf mehreren Knoten gleichzeitig ausgeführt werden. So vermeiden Sie Konflikte:

  • Schreiben Sie nur in einen Knoten.

  • Schreiben Sie in unabhängige Datenbankuntergruppen auf jedem Knoten (weisen Sie beispielsweise jedem Knoten ein separates Schema zu).

Verwenden Sie bei „INSERT vs. INSERT“-Konflikten globale Sequenzen, um Konflikte vollständig zu vermeiden.

Wenn Konflikte für Ihren Anwendungsfall nicht akzeptabel sind, sollten Sie die Implementierung verteilter Sperren auf Anwendungsebene erwägen. Häufig ist es am besten, die Anwendung so zu gestalten, dass sie mit den Konfliktlösungsmechanismen von pgactive funktioniert, anstatt zu versuchen, alle Konflikte zu vermeiden. Weitere Informationen finden Sie unter Typen von Konflikten.

Protokollierung von Konflikten

pgactivelink protokolliert Konfliktvorfälle in der Tabelle pgactive.pgactive_conflict_history, um Sie bei der Diagnose und Behandlung von active-active-Konflikten zu unterstützen. Die Konfliktprotokollierung in dieser Tabelle erfolgt nur, wenn Sie pgactive.log_conflicts_to_table auf „true“ festlegen. Die pgactive-Erweiterung protokolliert Konflikte auch dann in der PostgreSQL-Protokolldatei, wenn log_min_messages auf LOG oder lower festgelegt ist, und zwar unabhängig von der Einstellung pgactive.log_conflicts_to_table.

Verwenden Sie die Konfliktverlaufstabelle, um die folgenden Schritte auszuführen:

  • Messen, wie häufig die Anwendung Konflikte verursacht

  • Identifizieren, wo Konflikte auftreten

  • Verbessern der Anwendung zur Reduzierung der Konfliktrate

  • Erkennen von Fällen, in denen Konfliktlösungen nicht zu den gewünschten Ergebnissen führen

  • Ermitteln, wo benutzerdefinierte Konfliktauslöser oder Änderungen am Anwendungsdesign benötigt werden

Bei Zeilenkonflikten können Sie optional Zeilenwerte protokollieren. Dies wird durch die Einstellung pgactive.log_conflicts_to_table gesteuert. Beachten Sie Folgendes:

  • Dies ist eine globale datenbankweite Option.

  • Es gibt keine tabellenbezogene Kontrolle über die Protokollierung von Zeilenwerten.

  • Für Feldnummern, Array-Elemente oder Feldlängen gelten keine Beschränkungen.

  • Die Aktivierung dieser Funktion ist möglicherweise nicht ratsam, wenn Sie mit Zeilen mit mehreren Megabyte arbeiten, die Konflikte auslösen können.

Da die Konfliktverlaufstabelle Daten aus jeder Tabelle in der Datenbank (jede mit potenziell unterschiedlichen Schemas) enthält, werden protokollierte Zeilenwerte als JSON-Felder gespeichert. Die JSON-Datei wird mit row_to_json erstellt, ähnlich wie beim direkten Aufruf aus SQL. PostgreSQL stellt keine json_to_row-Funktion bereit, daher benötigen Sie tabellenspezifischen Code (in PL/pgSQL, PL/Python, PL/Perl usw.), um ein zusammengesetztes Tupel aus dem protokollierten JSON zu rekonstruieren.

Anmerkung

Die Unterstützung für benutzerdefinierte Konflikte ist als zukünftige Erweiterungsfunktion geplant.