Wie wir Lazy Replication für die Notfallwiederherstellung mit PostgreSQL verwendet haben

Wie wir Lazy Replication für die Notfallwiederherstellung mit PostgreSQL verwendet haben
Die Replikation ist kein Backup. Oder nicht? So haben wir die verzögerte Replikation zur Wiederherstellung genutzt, indem wir versehentlich Verknüpfungen gelöscht haben.

Infrastrukturspezialisten GitLab ist für die Arbeit verantwortlich GitLab.com - die größte GitLab-Instanz in der Natur. Mit 3 Millionen Nutzern und fast 7 Millionen Projekten ist es eine der größten Open-Source-SaaS-Sites mit dedizierter Architektur. Ohne das PostgreSQL-Datenbanksystem wird die GitLab.com-Infrastruktur nicht weit kommen, und was wir aus Gründen der Fehlertoleranz im Falle von Fehlern einfach nicht tun, kann zu Datenverlusten führen. Es ist unwahrscheinlich, dass eine solche Katastrophe eintreten wird, aber wir haben uns gut vorbereitet und uns mit verschiedenen Sicherungs- und Replikationsmechanismen eingedeckt.

Die Replikation ist nicht Ihr Datenbank-Backup-Tool (siehe unten). Aber jetzt werden wir sehen, wie man versehentlich gelöschte Daten mit Lazy Replication schnell wiederherstellen kann: on GitLab.com Benutzer Verknüpfung entfernt für das Projekt gitlab-ce und verlorene Verbindungen mit Zusammenführungsanfragen und -aufgaben.

Mit der verzögerten Replik konnten wir Daten in nur 1,5 Stunden wiederherstellen. Sehen Sie, wie es war.

Point-in-Time-Wiederherstellung mit PostgreSQL

PostgreSQL verfügt über eine integrierte Funktion, die den Zustand einer Datenbank zu einem bestimmten Zeitpunkt wiederherstellt. Es wird genannt Wiederherstellung zu einem bestimmten Zeitpunkt (PITR) und nutzt die gleichen Mechanismen, die ein Replikat auf dem neuesten Stand halten: Beginnend mit einem zuverlässigen Snapshot des gesamten Datenbankclusters (Basis-Backup) wenden wir eine Reihe von Zustandsänderungen bis zu einem bestimmten Zeitpunkt an.

Um diese Funktion für ein Cold-Backup zu nutzen, erstellen wir regelmäßig ein Basis-Datenbank-Backup und speichern es in einem Archiv (in dem sich die GitLab-Archive befinden). Google Cloud-Speicher). Wir überwachen auch Änderungen des Datenbankstatus, indem wir ein Write-Ahead-Protokoll archivieren (Vorschreibeprotokoll, WAL). Und mit all dem können wir PITR für die Notfallwiederherstellung durchführen: Wir beginnen mit einem Snapshot, der vor dem Fehler erstellt wurde, und übernehmen die Änderungen aus dem WAL-Archiv bis zum Absturz.

Was ist verzögerte Replikation?

Unter verzögerter Replikation versteht man die verzögerte Anwendung von Änderungen aus WAL. Das heißt, die Transaktion erfolgte zur vollen Stunde X, aber es wird mit einer Verzögerung in der Replik angezeigt d rechtzeitig X + d.

Es gibt zwei Möglichkeiten, eine physische Datenbankreplik in PostgreSQL einzurichten: Archivwiederherstellung und Streaming-Replikation. Wiederherstellung aus einem Archiv, funktioniert im Wesentlichen wie PITR, aber kontinuierlich: Wir extrahieren ständig Änderungen aus dem WAL-Archiv und wenden sie auf das Replikat an. A Streaming-Replikation ruft den WAL-Stream direkt vom Upstream-Datenbankhost ab. Wir bevorzugen die Wiederherstellung aus einem Archiv – es ist einfacher zu verwalten und bietet eine normale Leistung, die nicht hinter einem Produktionscluster zurückbleibt.

So richten Sie eine verzögerte Backup-Wiederherstellung ein

Wiederherstellungsoptionen in der Datei beschrieben recovery.conf. Beispiel:

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

Mit diesen Einstellungen haben wir eine verzögerte Replik mit Archivwiederherstellung konfiguriert. Wird hier verwendet Wal-e um WAL-Segmente zu extrahieren (restore_command) aus dem Archiv, und die Änderungen werden nach acht Stunden übernommen (recovery_min_apply_delay). Das Replikat überwacht das Archiv auf Zeitachsenänderungen, beispielsweise aufgrund eines Cluster-Failovers (recovery_target_timeline).

С recovery_min_apply_delay Sie können die Latenz-Streaming-Replikation einrichten, es gibt jedoch einige Fallstricke im Zusammenhang mit Replikationsslots, Hot-Spare-Feedback usw. Das WAL-Archiv vermeidet sie.

Parameter recovery_min_apply_delay erschien nur in PostgreSQL 9.3. In früheren Versionen erforderte die verzögerte Replikation eine Kombination aus Wiederherstellungsmanagementfunktionen (pg_xlog_replay_pause(), pg_xlog_replay_resume()) oder halten Sie die WAL-Segmente für die Dauer der Verzögerung im Archiv.

Wie macht PostgreSQL das?

Es ist interessant zu sehen, wie PostgreSQL Lazy Restore implementiert. Schauen wir uns an recoveryApplyDelay(XlogReaderState). Es heißt von Wiederholung der Hauptschleife für jeden Eintrag von WAL.

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;
}

Die Quintessenz ist, dass die Verzögerung auf der physischen Zeit basiert, die im Zeitstempel des Transaktions-Commits aufgezeichnet ist (xtime). Wie Sie sehen, gilt die Verzögerung nur für Commits und wirkt sich nicht auf andere Datensätze aus – alle Änderungen werden direkt angewendet und der Commit ist verzögert, sodass wir die Änderungen erst nach der konfigurierten Verzögerung sehen.

So verwenden Sie Lazy Replica für die Datenwiederherstellung

Nehmen wir an, wir haben einen Datenbankcluster in Produktion und ein Replikat mit einer Verzögerung von acht Stunden. Sehen wir uns anhand eines Beispiels an, wie man Daten wiederherstellt versehentliches Löschen von Verknüpfungen.

Als uns das Problem bewusst wurde, haben wir Backup-Wiederherstellung angehalten für verzögerte Replik:

SELECT pg_xlog_replay_pause();

Bei einer Pause bestand für uns kein Risiko, dass die Replik die Anfrage wiederholt DELETE. Nützliche Sache, wenn Sie Zeit brauchen, um alles herauszufinden.

Die Quintessenz ist, dass die verzögerte Replik den Moment vor der Anfrage erreichen muss DELETE. Wir kannten ungefähr den physischen Zeitpunkt der Entfernung. Wurden bewegt recovery_min_apply_delay und hinzugefügt recovery_target_time в recovery.conf. So kommt die Replik ohne Verzögerung zum richtigen Zeitpunkt:

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

Bei Zeitstempeln ist es besser, den Überschuss zu reduzieren, um nichts zu verpassen. Es stimmt, je größer der Rückgang, desto mehr Daten gehen uns verloren. Nochmals, wenn wir die Anfrage überspringen DELETE, wird alles wieder gelöscht und Sie müssen von vorne beginnen (oder sogar ein Kalt-Backup für PITR erstellen).

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

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 anpassen recovery_target_actionum die Instanz nach einem erneuten Versuch zu schließen, hochzustufen oder anzuhalten (standardmäßig wird sie angehalten).

Die Datenbank gelangte bereits vor dieser unglückseligen Anfrage in den Zustand. Jetzt können Sie beispielsweise Daten exportieren. Wir haben die Remote-Label-Daten und alle Links zu Issues und Merge Requests exportiert und in die Produktionsdatenbank übertragen. Wenn die Verluste groß sind, können Sie die Replik einfach bewerben und als Hauptversion verwenden. Aber dann werden alle Änderungen nach dem Moment, zu dem wir uns erholt haben, verloren gehen.

Es ist besser, Transaktions-IDs anstelle von Zeitstempeln zu verwenden. Es ist sinnvoll, diese IDs beispielsweise für DDL-Anweisungen aufzuzeichnen (z. B DROP TABLE), mit Hilfe log_statements = 'ddl'. Wenn wir eine Transaktions-ID hätten, würden wir sie nehmen recovery_target_xid und habe vor der Anfrage alles bis zur Transaktion ausgeführt DELETE.

Die Rückkehr zur Arbeit ist ganz einfach: Entfernen Sie alle Änderungen aus recovery.conf und starten Sie Postgres neu. Bald wird der Hinweis erneut eine Verzögerung von acht Stunden haben, und wir sind auf künftige Probleme vorbereitet.

Erholungsvorteile

Mit einem verzögerten Replikat müssen Sie anstelle eines Cold-Backups nicht stundenlang den gesamten Snapshot aus dem Archiv wiederherstellen. Beispielsweise benötigen wir fünf Stunden, um das gesamte 2-TB-Basis-Backup zu erhalten. Und dann müssen Sie (im schlimmsten Fall) immer noch die gesamte tägliche WAL anwenden, um den gewünschten Zustand wiederherzustellen.

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

  1. Sie müssen nicht das gesamte Basis-Backup aus dem Archiv abrufen.
  2. Es gibt ein festes Acht-Stunden-Fenster für WAL-Segmente, die wiederholt werden müssen.

Außerdem prüfen wir ständig, ob WAL PITRed sein kann, und wir würden Beschädigungen oder andere Probleme mit dem WAL-Archiv schnell bemerken, indem wir den Rückstand des verzögerten Replikats überwachen.

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

Zusammenfassung: Wo ist eine verzögerte Replik sinnvoll (und wo nicht)

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

Aber bedenken Sie: Replikation ist kein Backup.

Backup und Replikation dienen unterschiedlichen Zwecken. Ein Kalt-Backup ist praktisch, wenn Sie es versehentlich erstellt haben DELETE oder DROP TABLE. Wir erstellen ein Backup aus dem Cold Storage und stellen den vorherigen Zustand einer Tabelle oder einer gesamten Datenbank wieder her. Aber gleichzeitig die Bitte DROP TABLE fast sofort in allen Replikaten im Arbeitscluster reproduziert, sodass die reguläre Replikation hier nicht gespeichert wird. Die Replikation selbst hält die Datenbank bei der Vermietung einzelner Server verfügbar und verteilt die Last.

Selbst bei einer verzögerten Replik benötigen wir manchmal unbedingt ein Cold-Backup an einem sicheren Ort, wenn plötzlich ein Rechenzentrumsausfall, versteckte Schäden oder andere Ereignisse auftreten, die Sie nicht sofort bemerken. Hier macht eine Replikation keinen Sinn.

Beachten. Auf GitLab.com Wir schützen derzeit nur vor Datenverlust auf Systemebene und stellen keine Daten auf Benutzerebene wieder her.

Source: habr.com

Kommentar hinzufügen