Wie wir die verzögerte Replikation für die Notfallwiederherstellung mit PostgreSQL verwendet haben


Die Replikation ist keine Sicherung. Oder nicht? So haben wir die verzögerte Replikation für die Wiederherstellung verwendet, indem wir versehentlich Verknüpfungen gelöscht haben.


Die Infrastrukturspezialisten von GitLab sind für die Ausführung von GitLab.com verantwortlich , der größten Instanz von GitLab in der Natur. Es gibt 3 Millionen Benutzer und fast 7 Millionen Projekte. Dies ist eine der größten Open-Source-SaaS-Sites mit einer dedizierten Architektur. Ohne das PostgreSQL-Datenbanksystem wird die GitLab.com-Infrastruktur nicht weit gehen, und wir tun dies nur nicht aus Gründen der Fehlertoleranz, falls Fehler auftreten, wenn Daten verloren gehen können. Es ist unwahrscheinlich, dass eine solche Katastrophe eintreten wird, aber wir sind gut vorbereitet und mit verschiedenen Sicherungs- und Replikationsmechanismen ausgestattet.


Die Replikation ist für Sie kein Datenbanksicherungstool ( siehe unten ). Jetzt werden wir sehen, wie versehentlich gelöschte Daten mithilfe einer verzögerten Replikation schnell wiederhergestellt werden können: Auf GitLab.com hat der Benutzer die Verknüpfung für das gitlab-ce Projekt gelöscht und den Kontakt zu Zusammenführungsanforderungen und -aufgaben verloren.


Mit einem verzögerten Replikat haben wir Daten in nur 1,5 Stunden wiederhergestellt. Sehen Sie, wie es war.


Wiederherstellung zu einem bestimmten Zeitpunkt mit PostgreSQL


PostgreSQL verfügt über eine integrierte Funktion, die den Status der Datenbank zu einem bestimmten Zeitpunkt wiederherstellt. Es wird als Point-in-Time-Recovery (PITR) bezeichnet und verwendet dieselben Mechanismen, die die Relevanz des Replikats erhalten: Ausgehend von einem zuverlässigen Snapshot des gesamten Datenbankclusters (Basissicherung) wenden wir eine Reihe von Statusänderungen bis zu einem bestimmten Zeitpunkt an.


Um diese Funktion für eine kalte Sicherung zu verwenden, erstellen wir regelmäßig eine grundlegende Datenbanksicherung und speichern sie in einem Archiv (GitLab-Archive befinden sich live im Google Cloud-Speicher ). Wir überwachen auch Änderungen im Status der Datenbank, indem wir das WAL -Protokoll ( Write-Ahead-Protokoll ) archivieren. Und mit all dem können wir PITR für die Notfallwiederherstellung durchführen: Wir beginnen mit dem vor dem Fehler aufgenommenen Bild und übernehmen die Änderungen aus dem WAL-Archiv bis zum Fehler.


Was ist eine verzögerte Replikation?


Die verzögerte Replikation ist die Anwendung verzögerter WAL-Änderungen. Das heißt, die Transaktion wurde zur Stunde X , sie wird jedoch im Replikat mit einer Verzögerung von d zur Stunde X + d .


PostgreSQL bietet zwei Möglichkeiten, um das physische Replikat der Datenbank zu konfigurieren: Wiederherstellung aus dem Archiv und Streaming-Replikation. Das Wiederherstellen aus dem Archiv funktioniert zwar wie PITR, jedoch kontinuierlich: Wir extrahieren ständig die Änderungen aus dem WAL-Archiv und wenden sie auf das Replikat an. Durch die Streaming-Replikation wird der WAL-Stream direkt vom Upstream-Datenbankhost abgerufen. Wir bevorzugen die Wiederherstellung aus dem Archiv - es ist einfacher zu verwalten und weist eine normale Leistung auf, die nicht hinter dem Arbeitscluster zurückbleibt.


So richten Sie die verzögerte Wiederherstellung aus dem Archiv ein


Wiederherstellungsoptionen werden in der Datei recovery.conf beschrieben. Ein Beispiel:


 standby_mode = 'on' restore_command = '/usr/bin/envdir /etc/wal-ed/env /opt/wal-e/bin/wal-e wal-fetch -p 4 "%f" "%p"' recovery_min_apply_delay = '8h' recovery_target_timeline = 'latest' 

Mit diesen Parametern haben wir ein verzögertes Replikat mit Wiederherstellung aus dem Archiv eingerichtet. Hier wird wal-e verwendet , um WAL-Segmente ( restore_command ) aus dem Archiv zu extrahieren, und die Änderungen werden nach acht Stunden übernommen ( recovery_min_apply_delay ). Das Replikat überwacht Änderungen in der Zeitleiste im Archiv, z. B. aufgrund eines Failovers im Cluster ( recovery_target_timeline ).


Mit recovery_min_apply_delay Sie die verzögerte Streaming-Replikation konfigurieren. Es gibt jedoch einige Tricks, die mit Replikationssteckplätzen, Hot-Spare-Feedback usw. verbunden sind. Mit dem WAL-Archiv können Sie diese vermeiden.


Der Parameter recovery_min_apply_delay nur in PostgreSQL 9.3 angezeigt. In früheren Versionen müssen Sie für die verzögerte Replikation eine Kombination von Wiederherstellungsverwaltungsfunktionen ( pg_xlog_replay_pause(), pg_xlog_replay_resume() ) pg_xlog_replay_pause(), pg_xlog_replay_resume() oder WAL-Segmente für die Zeitverzögerung im Archiv pg_xlog_replay_pause(), pg_xlog_replay_resume() .


Wie macht PostgreSQL das?


Neugierig zu sehen, wie PostgreSQL die verzögerte Wiederherstellung implementiert. Schauen wir uns recoveryApplyDelay(XlogReaderState) . Es wird von der Hauptschleife für jeden Eintrag in der WAL aufgerufen.


 static bool recoveryApplyDelay(XLogReaderState *record) { uint8 xact_info; TimestampTz xtime; long secs; int microsecs; /* nothing to do if no delay configured */ if (recovery_min_apply_delay <= 0) return false; /* no delay is applied on a database not yet consistent */ if (!reachedConsistency) return false; /* * Is it a COMMIT record? * * We deliberately choose not to delay aborts since they have no effect on * MVCC. We already allow replay of records that don't have a timestamp, * so there is already opportunity for issues caused by early conflicts on * standbys. */ if (XLogRecGetRmid(record) != RM_XACT_ID) return false; xact_info = XLogRecGetInfo(record) & XLOG_XACT_OPMASK; if (xact_info != XLOG_XACT_COMMIT && xact_info != XLOG_XACT_COMMIT_PREPARED) return false; if (!getRecordTimestamp(record, &xtime)) return false; recoveryDelayUntilTime = TimestampTzPlusMilliseconds(xtime, recovery_min_apply_delay); /* * Exit without arming the latch if it's already past time to apply this * record */ TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime, &secs, &microsecs); if (secs <= 0 && microsecs <= 0) return false; while (true) { // Shortened: // Use WaitLatch until we reached recoveryDelayUntilTime // and then break; } return true; } 

Unter dem Strich basiert die Verzögerung auf der physischen Zeit, die im Transaktions-Commit-Zeitstempel ( xtime ) xtime . Wie Sie sehen, gilt die Verzögerung nur für Festschreibungen und berührt keine anderen Datensätze. Alle Änderungen werden direkt angewendet, und die Festschreibung wird verzögert, sodass die Änderungen erst nach der Konfiguration der Verzögerung angezeigt werden.


Verwendung von Lazy Replica zum Wiederherstellen von Daten


Angenommen, wir haben einen Datenbankcluster in der Produktion und ein Replikat mit einer Verzögerung von acht Stunden. Lassen Sie uns anhand des Beispiels zum versehentlichen Löschen von Verknüpfungen sehen, wie Daten wiederhergestellt werden.


Als wir von dem Problem erfuhren , haben wir die Wiederherstellung aus dem Archiv für das faule Replikat angehalten :


 SELECT pg_xlog_replay_pause(); 

Mit einer Pause hatten wir kein Risiko, dass das Replikat die DELETE Anforderung wiederholt. Nützliche Sache, wenn Sie Zeit brauchen, um es herauszufinden.


Unter dem Strich muss das zurückgestellte Replikat den Punkt vor der DELETE Anforderung erreichen. Wir kannten ungefähr den physischen Zeitpunkt der Entfernung. Wir haben recovery_min_apply_delay entfernt und recovery_target_time zur recovery.conf hinzugefügt. So erreicht die Replik unverzüglich den richtigen Moment:


 recovery_target_time = '2018-10-12 09:25:00+00' 

Bei Zeitstempeln ist es besser, den Überschuss zu reduzieren, um ihn nicht zu verpassen. Je größer die Abnahme, desto mehr Daten verlieren wir. Wenn wir die DELETE Anforderung durchlaufen, wird alles erneut gelöscht und Sie müssen erneut beginnen (oder sogar ein kaltes Backup für PITR erstellen).


Wir haben die verzögerte Instanz von Postgres neu gestartet und die WAL-Segmente wurden bis zum angegebenen Zeitpunkt wiederholt. Sie können den Fortschritt in dieser Phase auf Anfrage verfolgen:


 SELECT -- current location in WAL pg_last_xlog_replay_location(), -- current transaction timestamp (state of the replica) pg_last_xact_replay_timestamp(), -- current physical time now(), -- the amount of time still to be applied until recovery_target_time has been reached '2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay; 

Wenn sich der Zeitstempel nicht mehr ändert, ist die Wiederherstellung abgeschlossen. Sie können die Aktion recovery_target_action so konfigurieren, dass eine Instanz nach einer Wiedergabe geschlossen, erweitert oder angehalten wird (standardmäßig wird sie angehalten).


Die Datenbank wurde vor dieser unglücklichen Anfrage in einen Zustand versetzt. Jetzt können Sie beispielsweise Daten exportieren. Wir haben die gelöschten Daten über die Verknüpfung und alle Verbindungen mit Aufgaben und Zusammenführungsanforderungen exportiert und in die Arbeitsdatenbank übertragen. Wenn die Verluste groß sind, können Sie das Replikat einfach bewerben und als Hauptreplikat verwenden. Aber dann gehen alle Änderungen nach dem Moment verloren, in dem wir uns erholt haben.


Anstelle von Zeitstempeln ist es besser, Transaktions-IDs zu verwenden. Es ist nützlich, diese IDs beispielsweise für DDL-Anweisungen (wie DROP TABLE ) mit log_statements = 'ddl' zu schreiben. Wenn wir eine Transaktions-ID hätten, würden wir recovery_target_xid nehmen und alles bis zur Transaktion vor der DELETE Anforderung ausführen.


Die Rückkehr zur Arbeit ist sehr einfach: Entfernen Sie alle Änderungen aus der Datei recovery.conf und starten Sie Postgres neu. Bald wird wieder eine Verzögerung von acht Stunden im Cue erscheinen, und wir sind bereit für zukünftige Probleme.


Wiederherstellungsvorteile


Bei einem verzögerten Replikat müssen Sie anstelle eines kalten Backups nicht stundenlang das gesamte Image aus dem Archiv wiederherstellen. Zum Beispiel benötigen wir fünf Stunden, um die gesamte Basissicherung von 2 TB zu erhalten. Und dann müssen Sie immer noch die gesamte tägliche WAL anwenden, um den gewünschten Zustand wiederherzustellen (im schlimmsten Fall).


Ein verzögertes Replikat ist in zweierlei Hinsicht besser als ein kaltes Backup:


  1. Es ist nicht erforderlich, die gesamte Basissicherung aus dem Archiv abzurufen.
  2. Es gibt ein festes Acht-Stunden-Fenster für WAL-Segmente, die wiederholt werden müssen.

Und wir überprüfen ständig, ob es möglich ist, PITR aus WAL zu erstellen, und wir würden schnell Schäden oder andere Probleme mit dem WAL-Archiv bemerken und auf die Verzögerung des verzögerten Replikats achten.


In diesem Beispiel dauerte die Wiederherstellung 50 Minuten, dh die Geschwindigkeit betrug 110 GB WAL-Daten pro Stunde (das Archiv befand sich damals noch in AWS S3 ). Insgesamt haben wir das Problem gelöst und die Daten in 1,5 Stunden wiederhergestellt.


Fazit: Wo die verzögerte Replik nützlich ist (und wo nicht)


Verwenden Sie die verzögerte Replikation als erste Hilfe, wenn Sie versehentlich Daten verlieren und diese Katastrophe innerhalb der konfigurierten Verzögerung bemerken.


Beachten Sie jedoch, dass die Replikation keine Sicherung ist.

Backup und Replikation haben unterschiedliche Ziele. Eine kalte Sicherung ist nützlich, wenn Sie versehentlich eine DELETE oder DROP TABLE . Wir machen eine Sicherung aus dem Kühlhaus und stellen den vorherigen Status der Tabelle oder der gesamten Datenbank wieder her. Gleichzeitig wird die DROP TABLE Abfrage fast sofort in allen Replikaten des Arbeitsclusters abgespielt, sodass Sie hier nicht durch regelmäßige Replikation gespeichert werden. Durch die Replikation selbst bleibt die Datenbank zugänglich, wenn separate Server geleast werden, und die Last wird verteilt.


Selbst bei einem verzögerten Replikat benötigen wir manchmal wirklich ein kaltes Backup an einem sicheren Ort, wenn ein Rechenzentrum abstürzt, versteckte Schäden oder andere Ereignisse auftreten, die Sie nicht sofort bemerken. Eine Replikation macht keinen Sinn.


Hinweis Bei GitLab.com schützen wir jetzt nur auf Systemebene vor Datenverlust und stellen keine Daten auf Benutzerebene wieder her.

Source: https://habr.com/ru/post/de445446/


All Articles