PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Verwenden der logischen PostgreSQL-Replikation zur Aufrechterhaltung eines stets aktuellen Lese-/Schreib-TEST-Servers

In diesem Blogeintrag sprechen wir über die logische Replikation in PostgreSQL:ihre Anwendungsfälle, allgemeine Informationen zum Status dieser Technologie und einen speziellen Anwendungsfall, insbesondere zum Einrichten eines Abonnenten-(Replik-)Knotens des Primärservers in der Reihenfolge als Datenbankserver für die Testumgebung zu fungieren, und die bewältigten Herausforderungen.

Einleitung

Die offiziell in PostgreSQL 10 eingeführte logische Replikation ist die neueste Replikationstechnologie, die von der PostgreSQL-Community angeboten wird. Die logische Replikation ist eine Fortsetzung des Erbes der physischen Replikation, mit der sie viele Ideen und Code teilt. Die logische Replikation funktioniert wie die physische Replikation unter Verwendung der WAL, um logische Änderungen unabhängig von der Version oder spezifischen Architektur aufzuzeichnen. Um eine logische Replikation für das Kernangebot bereitstellen zu können, hat die PostgreSQL-Community einen langen Weg zurückgelegt.

Replikationstypen und Verlauf der PostgreSQL-Replikation

Die Replikationsarten in Datenbanken können wie folgt klassifiziert werden:

  • Physische (AKA binäre) Replikation
    • Betriebssystemebene (vSphere-Replikation)
    • Dateisystemebene (DRBD)
    • Datenbankebene (WAL-basiert)
  • Logische Replikation (Datenbankebene)
    • Trigger-basiert (DBMirror, Slony)
    • Middleware (pgpool)
    • WAL-basiert (pglogical, Logical Replication)

Die Roadmap, die zur heutigen WAL-basierten logischen Replikation führt, lautete:

  • 2001:DBMirror (triggerbasiert)
  • 2004:Slony1 (triggerbasiert), pgpool (Middleware)
  • 2005:PITR (WAL-basiert) eingeführt in PostgreSQL 8.0
  • 2006:Warm-Standby in PostgreSQL 8.2
  • 2010:Physische Streaming-Replikation, Hot-Standby in PostgreSQL 9.0
  • 2011:Synchrone Streaming-Replikation in PostgreSQL 9.1
  • 2012:Kaskadierende Streaming-Replikation in PostgreSQL 9.2
  • 2013:Hintergrundarbeiter in PostgreSQL 9.3
  • 2014:Logische Dekodierungs-API, Replikationsslots. (Die Grundlagen für die logische Replikation) in PostgreSQL 9.4
  • 2015:2ndQuadrant stellt pglogical vor, den Vorfahren der logischen Replikation
  • 2017:Logische Replikation in Core PostgreSQL 10!

Wie wir sehen können, haben viele Technologien zusammengearbeitet, um die logische Replikation Wirklichkeit werden zu lassen:WAL-Archivierung, Warm/Hot-Standbys, physische WAL-Replikation, Hintergrundarbeiter, logische Dekodierung. Unter der Annahme, dass der Leser mit den meisten Begriffen der physischen Replikation vertraut ist, werden wir über die grundlegenden Komponenten der logischen Replikation sprechen.

Grundlegende Konzepte der logischen PostgreSQL-Replikation

Etwas Terminologie:

  • Veröffentlichung: Eine Reihe von Änderungen aus einer Reihe von Tabellen, die in einer bestimmten Datenbank auf einem physischen Replikations-Primärserver definiert sind. Eine Veröffentlichung kann alle oder einige von Folgendem handhaben:INSERT, DELETE, UPDATE, TRUNCATE.
  • Publisher-Knoten: Der Server, auf dem sich die Veröffentlichung befindet.
  • Nachbildung der Identität: Eine Möglichkeit, die Zeile auf der Abonnentenseite für UPDATEs und DELETEs zu identifizieren.
  • Abonnement: Eine Verbindung zu einem Herausgeberknoten und einer oder mehreren darin enthaltenen Veröffentlichungen. Ein Abonnement verwendet einen dedizierten Replikationsslot auf dem Herausgeber für die Replikation. Zusätzliche Replikationsslots können für den anfänglichen Synchronisationsschritt verwendet werden.
  • Abonnentenknoten: Der Server, auf dem sich das Abonnement befindet.

Die logische Replikation folgt einem Publish/Subscribe-Modell. Ein oder mehrere Abonnenten können eine oder mehrere Veröffentlichungen auf einem Herausgeberknoten abonnieren. Abonnenten können erneut veröffentlichen, um eine kaskadierende Replikation zu ermöglichen. Die logische Replikation einer Tabelle besteht aus zwei Phasen:

  • Erstellen eines Snapshots der Tabelle auf dem Herausgeber und Kopieren auf den Abonnenten
  • Übernehmen aller Änderungen (seit dem Snapshot) in derselben Reihenfolge

Die logische Replikation ist transaktional und garantiert, dass die Reihenfolge der auf den Abonnenten angewendeten Änderungen dieselbe bleibt wie auf dem Verleger. Die logische Replikation bietet viel mehr Freiheit als die physische (binäre) Replikation und kann daher auf mehrere Arten verwendet werden:

  • Einzeldatenbank- oder tabellenspezifische Replikation (keine Notwendigkeit, den gesamten Cluster zu replizieren)
  • Auslöser für den Abonnenten für eine bestimmte Aufgabe festlegen (z. B. Anonymisierung, ein ziemlich heißes Thema nach Inkrafttreten der DSGVO)
  • Wenn ein Abonnentenknoten Daten von vielen Herausgeberknoten sammelt, ermöglicht dies eine zentrale analytische Verarbeitung
  • Replikation zwischen verschiedenen Versionen/Architekturen/Plattformen (Upgrades ohne Ausfallzeit)
  • Verwenden des Teilnehmerknotens als Datenbankserver für eine Test-/Entwicklungsumgebung. Wir wollen das, weil das Testen mit echten Daten die realistischste Art von Tests ist.

Vorbehalte und Einschränkungen

Es gibt bestimmte Dinge, die wir bei der Verwendung der logischen Replikation beachten müssen, einige davon können einige Entwurfsentscheidungen beeinflussen, andere können zu kritischen Vorfällen führen.

Einschränkungen

  • Nur DML-Operationen werden unterstützt. Kein DDL. Das Schema muss vorher definiert werden
  • Sequenzen werden nicht repliziert
  • Große Objekte werden nicht repliziert
  • Nur einfache Basistabellen werden unterstützt (materialisierte Ansichten, Partitionsstammtabellen, Fremdtabellen werden nicht unterstützt)

Warnhinweise

Das grundlegende Problem, mit dem wir uns früher oder später bei der Verwendung der logischen Replikation auseinandersetzen müssen, sind Konflikte auf dem Abonnenten. Der Abonnent ist ein normaler Lese-/Schreibserver, der als Primärserver in einer physischen Replikationskonfiguration oder sogar als Herausgeber in einer kaskadierenden logischen Replikationskonfiguration fungieren kann. Solange Schreibvorgänge auf die abonnierten Tabellen ausgeführt werden, kann es zu Konflikten kommen . Ein Konflikt entsteht, wenn replizierte Daten gegen eine Einschränkung der Tabelle verstoßen, auf die sie angewendet werden. Normalerweise ist die Operation, die dies verursacht, INSERT, DELETES oder UPDATES, die aufgrund fehlender Zeilen keine Wirkung haben und keinen Konflikt verursachen. Wenn ein Konflikt auftritt, stoppt die Replikation. Der logische Hintergrund-Worker wird im angegebenen Intervall (wal_retrieve_retry_interval) neu gestartet, die Replikation schlägt jedoch erneut fehl, bis die Ursache des Konflikts behoben ist. Dies ist ein kritischer Zustand, der sofort behandelt werden muss. Andernfalls bleibt der Replikationsslot hängen An seiner aktuellen Position beginnt der Publisher-Knoten mit dem Sammeln von WALs und dem Publisher-Knoten wird unweigerlich kein Speicherplatz mehr zur Verfügung stehen . Ein Konflikt ist der häufigste Grund, warum die Replikation möglicherweise beendet wird, aber jede andere fehlerhafte Bedingung hat die gleiche Wirkung:z. Wir haben eine neue NOT NULL-Spalte in einer abonnierten Tabelle hinzugefügt, aber vergessen, einen Standardwert zu definieren, oder eine Spalte in einer veröffentlichten Tabelle hinzugefügt, aber vergessen, sie in der abonnierten Tabelle zu definieren, oder einen Fehler bei ihrem Typ gemacht, und die beiden Typen sind es nicht kompatibel. All diese Fehler stoppen die Replikation. Es gibt zwei Möglichkeiten, einen Konflikt zu lösen:

  1. Das eigentliche Problem lösen
  2. Überspringen Sie die fehlgeschlagene Transaktion, indem Sie pg_replication_origin_advance aufrufen

Lösung b. wie auch hier gezeigt, kann gefährlich und knifflig sein, da es im Grunde ein Trial-and-Error-Prozess ist, und wenn man die aktuelle LSN auf dem Herausgeber wählt, kann man leicht mit einem kaputten Replikationssystem enden, da es Operationen zwischen den problematischen LSN geben kann und die aktuelle LSN, die wir behalten möchten. Der beste Weg ist also, das Problem tatsächlich auf der Teilnehmerseite zu lösen. Z.B. Wenn eine UNIQUE KEY-Verletzung auftritt, aktualisieren wir möglicherweise die Daten des Abonnenten oder löschen einfach die Zeile. In einer Produktionsumgebung muss all dies automatisiert oder zumindest halbautomatisiert erfolgen.

Einrichten der Herausgeber- und Abonnentenknoten

Für einen allgemeinen Überblick über die logische Replikation in der Praxis lesen Sie bitte diesen Blog.

Die relevanten Parameter für die logische Replikation sind:

  • Publisher-Seite
    • wal_level>=„logisch“
    • max_replication_slots>=#subscriptions + anfängliche Tabellensynchronisierung
    • max_wal_senders>=max_replication_slots + other_physical_standbys
  • Teilnehmerseite
    • max_replication_slots>=#Abonnements
    • max_logical_replication_workers>=#subscriptions + anfängliche Tabellensynchronisierung
    • max_worker_processes>=max_logical_replication_workers + 1 + max_parallel_workers

Wir werden uns auf die besonderen Überlegungen konzentrieren, die sich aus unserem speziellen Zweck ergeben, den wir durch logische Replikation erreichen müssen:Erstellen eines Testdatenbank-Clusters zur Verwendung durch die Testabteilung . Die Veröffentlichung kann entweder für alle Tabellen oder tabellenweise definiert werden. Ich schlage den tabellenweisen Ansatz vor, da er uns maximale Flexibilität bietet. Die allgemeinen Schritte können wie folgt zusammengefasst werden:

  • Führen Sie eine neue initdb auf dem Abonnentenknoten durch
  • Speichern Sie das Schema des Publisher-Clusters und kopieren Sie es auf den Subscriber-Knoten
  • Erstellen Sie das Schema auf dem Abonnenten
  • Entscheiden Sie, welche Tische Sie benötigen und welche nicht.

In Bezug auf den obigen Aufzählungspunkt gibt es zwei Gründe, warum Sie möglicherweise keine Tabelle replizieren oder für die Replikation einrichten müssen:

  • Es ist eine Dummy-Tabelle ohne Bedeutung (und vielleicht sollten Sie sie auch aus der Produktion entfernen)
  • ist eine lokale Tabelle für die Produktionsumgebung, was bedeutet, dass es absolut sinnvoll ist, dass dieselbe Tabelle in der Testumgebung (Abonnent) ihre eigenen Daten hat

Alle Tabellen, die an der logischen Replikation teilnehmen, müssen eine REPLICA IDENTITY haben. Dies ist standardmäßig der PRIMARY KEY, und falls nicht verfügbar, kann ein UNIQUE-Schlüssel definiert werden. Nächster Schritt, um den Status der Tabellen in Bezug auf REPLICA IDENTITY zu finden.

  • Suchen Sie die Tabellen ohne offensichtlichen Kandidaten für REPLICA IDENTITY
    select table_schema||'.'||table_name from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name NOT IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY','UNIQUE')) AND table_schema NOT IN ('information_schema','pg_catalog') ;
  • Suchen Sie die Tabellen ohne PRIMARY KEY, aber mit einem UNIQUE INDEX
    select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'UNIQUE' EXCEPT select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'PRIMARY KEY';
  • Gehen Sie die obigen Listen durch und entscheiden Sie, was Sie mit jeder Tabelle tun möchten
  • Erstellen Sie die Publikation mit den Tabellen, für die ein PK existiert
    select 'CREATE PUBLICATION data_for_testdb_pub FOR TABLE ONLY ' || string_agg(qry.tblname,', ONLY ') FROM (select table_schema||'.'||quote_ident(table_name) as tblname from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY')) AND table_schema NOT IN( 'information_schema','pg_catalog')  ORDER BY 1) as qry;
    \gexec
  • Erstellen Sie dann das Abonnement auf dem Teilnehmerknoten
    create subscription data_for_testdb_pub CONNECTION 'dbname=yourdb host=yourdbhost user=repmgr' PUBLICATION data_for_testdb_pub ;
    Oben werden die Daten ebenfalls kopiert.
  • Fügen Sie gewünschte Tabellen hinzu, die einen EINZIGARTIGEN Index haben
    Führen Sie sowohl in Publisher- als auch Subscriber-Knoten aus, z. B.:
    ALTER TABLE someschema.yourtable REPLICA IDENTITY USING INDEX yourindex_ukey;
    Auf dem Herausgeber:
    ALTER PUBLICATION data_for_testdb_pub ADD TABLE ONLY someschema.yourtable;
    Auf dem Abonnenten:
    ALTER SUBSCRIPTION data_for_testdb_pub REFRESH PUBLICATION WITH ( COPY_DATA );
  • An dieser Stelle (Synchronisation) sollten Sie immer das PostgreSQL-Log auf dem Subscriber-Knoten im Auge behalten. Sie möchten keine Fehler oder irgendetwas (Timeout), das die Fortsetzung der logischen Replikation verhindert. JEDEN FEHLER SOFORT BEHEBEN , oder der Herausgeber sammelt weiterhin WAL-Dateien in pg_wal und hat schließlich keinen Platz mehr. Sie müssen sich also mit
      befassen
    • Alle FEHLER oder Nachrichten bezüglich des logischen Workers, die zum Beenden führen
    • Kümmere dich auch um
      • wal_receiver_timeout
      • wal_sender_timeout

Nachdem Sie alle Probleme gelöst haben, sollte Ihr Abonnentenknoten glücklich laufen. Die nächste Frage ist also, wie dies als Testdatenbankserver verwendet werden kann. Sie müssen sich mit diesen Problemen/Problemen befassen:

  1. Anonymisierung
  2. Primärschlüssel und eindeutige Schlüssel, die auf Sequenzverletzungen basieren
  3. Eine allgemeine Reihe bewährter Verfahren
  4. Überwachung

Anonymisierung

In Bezug auf die Anonymisierung personenbezogener Daten, die durch die DSGVO in der EU erzwungen wird, sollten Sie IMMER einige Auslöser schreiben, die alle Felder in Bezug auf Adressen, Bankkonten, Familienstand, Telefonnummern, E-Mails usw. ausblenden. Wenden Sie sich diesbezüglich an Ihren Sicherheitsbeauftragten in Ihrem Unternehmen was behalten und was ausblenden. Die Trigger sollten als ALWAYS definiert werden, da der logische Worker die Anweisungen als REPLICA ausführt.

Primärschlüssel mit Sequenzen

In Bezug auf Sequenzen wird es eindeutig ein Problem mit diesen Schlüsseln geben, wenn es nicht vor Beginn eines Tests behandelt wird. Betrachten Sie diesen Fall:

  • Am Freitagnachmittag führen Sie einige Tests in der Abonnentendatenbank durch, indem Sie eine neue Zeile in eine Tabelle einfügen. Diese hat als ID den nächsten Wert, der von der Sequenz generiert wird.
  • Du fährst übers Wochenende nach Hause.
  • Ein Produktionsbenutzer gibt eine Zeile in dieselbe Tabelle in der Publisher-Datenbank ein.
  • Die Zeile wird basierend auf der REPLICA IDENTITY an den Subscriber-Knoten repliziert, schlägt jedoch aufgrund eines PK-Verstoßes ERROR fehl. Der logische Hintergrund-Worker wird beendet und versucht es erneut. Wird aber weiterhin fehlschlagen, solange das Problem besteht.
  • Die Replikation bleibt hängen. Der Replikationsslot beginnt mit dem Sammeln von WALs.
  • Der Publisher hat keinen Speicherplatz mehr.
  • Am Wochenende erhalten Sie eine E-Mail, dass Ihr primärer Knoten in Panik geraten ist!

Um das Sequenzproblem zu lösen, können Sie also folgendermaßen vorgehen:

select 'SELECT setval(''' || seqrelid::regclass||''','||CASE WHEN seqincrement <0 THEN -214748364 ELSE 214748364 END||');' from pg_sequence where seqtypid=20;
\gexec

Was das Obige tut, ist, Sequenzen auf einen ausreichend großen Wert zu setzen, damit sie sich in Zukunft für ein ziemlich großes Fenster nie überlappen, was Ihnen einen problemlosen Testserver ermöglicht.

Eine Reihe bewährter Verfahren

Sie sollten Ihren Programmierern wirklich sagen, dass sie ihre Tests nicht persistent machen sollen. Daher sollte jeder Test nach Abschluss die Datenbank in demselben Zustand belassen, in dem sie sich vor dem Test befand. Bei sequenzbasierten ID-Einfügungen ist dies kein Problem, wir haben zuvor eine Lösung gesehen. Bei nicht aufeinanderfolgenden (z. B. zusammengesetzten) UNIQUE-Schlüsseln könnte dies jedoch ein Problem darstellen. Daher ist es am besten, diese Testdaten zu löschen, bevor eine Produktionszeile mit demselben Wert in die abonnierte Tabelle gelangt.

Hier sollten wir auch den Umgang mit Schemaänderungen hinzufügen. Alle Schemaänderungen müssen auch auf dem Abonnenten vorgenommen werden, um den replizierten DML-Datenverkehr nicht zu unterbrechen.

Laden Sie noch heute das Whitepaper PostgreSQL-Management und -Automatisierung mit ClusterControl herunterErfahren Sie, was Sie wissen müssen, um PostgreSQL bereitzustellen, zu überwachen, zu verwalten und zu skalierenLaden Sie das Whitepaper herunter

Überwachung

Sie sollten wirklich in eine gute Überwachungslösung investieren. Sie sollten auf ...

achten

Beim Abonnenten:

  • ALLE Nachrichten im Protokoll des Abonnenten, die für den logischen Worker-Exit relevant sind. Die Installation eines Tools wie tail_n_mail kann dabei wirklich helfen. Eine Konfiguration, die bekanntermaßen funktioniert:
    INCLUDE: ERROR:  .*publisher.*
    INCLUDE: ERROR:  .*exited with exit.*
    INCLUDE: LOG:  .*exited with exit.*
    INCLUDE: FATAL:  
    INCLUDE: PANIC:
    Sobald wir eine Benachrichtigung von tail_n_mail erhalten, sollten wir das Problem sofort lösen.
  • pg_stat_subscription. Pid sollte nicht null sein. Auch die Verzögerung sollte klein sein.

Beim Verlag:

  • pg_stat_replication. Dies sollte so viele Zeilen haben, wie sie sein sollten:Eine für jeden verbundenen Streaming-Replikations-Standby (Subscriber-Knoten und andere physische Standbys eingeschlossen).
  • pg_replication_slots für den Abonnenten-Slot. Dies sollte aktiv sein.

Im Allgemeinen dauert es einige Zeit, bis Sie Ihren idealen Testdatenbankserver ohne Probleme laufen haben, aber wenn Sie sie alle gelöst haben, werden Ihre Programmierer Ihnen dafür danken!