Hoe we vertraagde replicatie hebben gebruikt voor noodherstel met PostgreSQL

Hoe we vertraagde replicatie hebben gebruikt voor noodherstel met PostgreSQL
Replicatie is geen back-up. Of niet? Hier ziet u hoe we uitgestelde replicatie hebben gebruikt om te herstellen van het per ongeluk verwijderen van snelkoppelingen.

Infraspecialisten GitLab is verantwoordelijk voor de werkzaamheden GitLab.com - de grootste GitLab-instantie in de natuur. Met 3 miljoen gebruikers en bijna 7 miljoen projecten is het een van de grootste open source SaaS-sites met een speciale architectuur. Zonder het PostgreSQL-databasesysteem zal de GitLab.com-infrastructuur niet ver komen, en wat doen we om fouttolerantie te garanderen in geval van fouten waarbij gegevens verloren kunnen gaan. Het is onwaarschijnlijk dat een dergelijke ramp zich zal voordoen, maar we zijn goed voorbereid en beschikken over verschillende back-up- en replicatiemechanismen.

Replicatie is geen manier om een ​​back-up van databases te maken (zie hieronder). Maar nu zullen we zien hoe we per ongeluk verwijderde gegevens snel kunnen herstellen met behulp van luie replicatie: aan GitLab.com gebruiker heb de snelkoppeling verwijderd voor het project gitlab-ce en verloren verbindingen met samenvoegverzoeken en taken.

Met een uitgestelde replica hebben we de gegevens in slechts 1,5 uur hersteld. Kijk hoe het gebeurde.

Point-in-time herstel met PostgreSQL

PostgreSQL heeft een ingebouwde functie die de status van een database herstelt naar een specifiek tijdstip. Het heet Herstel naar een bepaald tijdstip (PITR) en gebruikt dezelfde mechanismen die de replica up-to-date houden: beginnend met een betrouwbare momentopname van het gehele databasecluster (basisback-up), passen we een reeks statusveranderingen toe tot een bepaald tijdstip.

Om deze functie voor koude back-up te gebruiken, maken we regelmatig een basisback-up van de database en slaan deze op in een archief (GitLab-archieven leven in Google-cloudopslag). We houden ook veranderingen in de status van de database in de gaten door het vooruitschrijflogboek te archiveren (vooruitschrijven log, WAL). En met dit alles kunnen we een PITR uitvoeren voor noodherstel: beginnend met de momentopname die vóór de storing is gemaakt, en het toepassen van de wijzigingen uit het WAL-archief tot aan de storing.

Wat is uitgestelde replicatie?

Luie replicatie is het met vertraging toepassen van wijzigingen uit WAL. Dat wil zeggen dat de transactie binnen een uur plaatsvond X, maar het verschijnt met vertraging in de replica d over een uur X + d.

PostgreSQL heeft twee manieren om een ​​fysieke databasereplica in te stellen: back-upherstel en streaming-replicatie. Herstellen vanuit een archief, werkt in essentie als PITR, maar continu: we halen voortdurend wijzigingen uit het WAL-archief en passen deze toe op de replica. A streaming-replicatie haalt de WAL-stream rechtstreeks op van de upstream-databasehost. Wij geven de voorkeur aan archiefherstel: het is gemakkelijker te beheren en levert normale prestaties die gelijke tred houden met het productiecluster.

Uitgesteld herstel vanuit een archief instellen

Herstelopties beschreven in het bestand recovery.conf. Voorbeeld:

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'

Met deze parameters hebben we een uitgestelde replica met back-upherstel geconfigureerd. Hier wordt het gebruikt wal-e om WAL-segmenten te extraheren (restore_command) uit het archief en wijzigingen worden na acht uur toegepast (recovery_min_apply_delay). De replica let op tijdlijnwijzigingen in het archief, bijvoorbeeld als gevolg van een clusterfailover (recovery_target_timeline).

С recovery_min_apply_delay U kunt streamingreplicatie met vertraging instellen, maar er zijn hier een aantal valkuilen die te maken hebben met replicatieslots, hot standby-feedback, enzovoort. Met het WAL-archief kunt u ze vermijden.

Parameter recovery_min_apply_delay verscheen alleen in PostgreSQL 9.3. In eerdere versies moest u voor uitgestelde replicatie de combinatie configureren herstelbeheerfuncties (pg_xlog_replay_pause(), pg_xlog_replay_resume()) of bewaar WAL-segmenten in het archief gedurende de duur van de vertraging.

Hoe doet PostgreSQL dit?

Het is interessant om te zien hoe PostgreSQL lui herstel implementeert. Laten we eens kijken recoveryApplyDelay(XlogReaderState). Er wordt gebeld van hoofdherhalingslus voor elke inzending van 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;
}

Het komt erop neer dat de vertraging gebaseerd is op de fysieke tijd die is vastgelegd in het tijdstempel van de transactievastlegging (xtime). Zoals je kunt zien, is de vertraging alleen van toepassing op commits en heeft deze geen invloed op andere items. Alle wijzigingen worden direct toegepast en de commit wordt uitgesteld, dus we zullen de wijzigingen pas zien na de geconfigureerde vertraging.

Hoe u een vertraagde replica gebruikt om gegevens te herstellen

Laten we zeggen dat we een databasecluster en een replica hebben met een productievertraging van acht uur. Laten we eens kijken hoe u gegevens kunt herstellen aan de hand van een voorbeeld per ongeluk snelkoppelingen verwijderen.

Toen we over het probleem hoorden, hebben we archiefherstel is gepauzeerd voor een uitgestelde replica:

SELECT pg_xlog_replay_pause();

Met een pauze hadden we geen risico dat de replica het verzoek zou herhalen DELETE. Handig als je tijd nodig hebt om alles uit te zoeken.

Het punt is dat de uitgestelde replica het moment vóór het verzoek moet bereiken DELETE. We kenden ongeveer het fysieke tijdstip van verwijdering. Wij hebben verwijderd recovery_min_apply_delay en voegde toe recovery_target_time в recovery.conf. Zo bereikt de replica zonder vertraging het juiste moment:

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

Met tijdstempels is het beter om het eigen risico te verminderen, zodat u het niet mist. Het is waar dat hoe groter de daling is, hoe meer gegevens we verliezen. Nogmaals, als we het verzoek missen DELETE, wordt alles weer verwijderd en moet je opnieuw beginnen (of zelfs een koude back-up maken voor PITR).

We hebben de uitgestelde Postgres-instantie opnieuw opgestart en de WAL-segmenten werden herhaald tot de opgegeven tijd. U kunt de voortgang in deze fase volgen door het volgende te vragen:

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;

Als de tijdstempel niet meer verandert, is het herstel voltooid. Actie kan worden aangepast recovery_target_actionom de instantie na een nieuwe poging te sluiten, te bevorderen of te onderbreken (deze wordt standaard opgeschort).

De database keerde terug naar de staat van vóór dat ongelukkige verzoek. Nu kunt u bijvoorbeeld gegevens exporteren. We hebben de verwijderde etiketgegevens en alle links naar uitgaven en samenvoegverzoeken geëxporteerd en naar de productiedatabase verplaatst. Als de verliezen grootschalig zijn, kunt u eenvoudig de replica promoten en als belangrijkste gebruiken. Maar dan zullen alle veranderingen na het punt waarop we zijn hersteld verloren gaan.

In plaats van tijdstempels is het beter om transactie-ID's te gebruiken. Het is handig om deze ID’s vast te leggen, bijvoorbeeld voor DDL-statements (zoals DROP TABLE), door het gebruiken van log_statements = 'ddl'. Als we een transactie-ID hadden, zouden we dat nemen recovery_target_xid en voerde alles uit tot aan de transactie vóór het verzoek DELETE.

Weer aan de slag gaan is heel eenvoudig: verwijder alle wijzigingen uit recovery.conf en herstart Postgres. De replica heeft binnenkort weer een vertraging van acht uur en we zijn voorbereid op toekomstige problemen.

Herstelvoordelen

Met een uitgestelde replica in plaats van een koude back-up hoeft u geen uren te besteden aan het herstellen van de volledige image uit het archief. Het kost ons bijvoorbeeld vijf uur om de volledige standaardback-up van 2 TB te krijgen. En dan moet je nog de hele dagelijkse WAL toepassen om (in het ergste geval) weer in de gewenste staat te komen.

Een uitgestelde replica is op twee manieren beter dan een koude back-up:

  1. Het is niet nodig om de volledige basisback-up uit het archief te verwijderen.
  2. Er is een vast venster van acht uur met WAL-segmenten dat moet worden herhaald.

We controleren ook voortdurend of het mogelijk is om van WAL een PITR te maken, en we zouden snel corruptie of andere problemen met het WAL-archief opmerken door de vertraging van de uitgestelde replica te monitoren.

In dit voorbeeld kostte het ons 50 minuten om te herstellen, wat betekent dat de snelheid 110 GB aan WAL-gegevens per uur bedroeg (het archief stond nog steeds aan AWS S3). In totaal hebben we het probleem opgelost en de gegevens binnen 1,5 uur hersteld.

Resultaten: waar een uitgestelde replica nuttig is (en waar niet)

Gebruik vertraagde replicatie als eerste hulp als u per ongeluk gegevens bent kwijtgeraakt en dit probleem binnen de geconfigureerde vertraging hebt opgemerkt.

Maar onthoud: replicatie is geen back-up.

Back-up en replicatie hebben verschillende doeleinden. Een koude back-up is handig als u deze per ongeluk hebt gemaakt DELETE of DROP TABLE. Wij maken een back-up van de koude opslag en herstellen de vorige staat van de tabel of de gehele database. Maar tegelijkertijd het verzoek DROP TABLE wordt vrijwel onmiddellijk gereproduceerd in alle replica's op het werkcluster, dus gewone replicatie zal hier niet helpen. Replicatie zelf houdt de database beschikbaar wanneer individuele servers worden verhuurd en verdeelt de belasting.

Zelfs met een uitgestelde replica hebben we soms echt een koude back-up op een veilige plek nodig als er zich een datacenterstoring, verborgen schade of andere gebeurtenissen voordoen die niet direct merkbaar zijn. Replicatie alleen heeft hier geen zin.

Noot. op GitLab.com We beschermen momenteel alleen tegen gegevensverlies op systeemniveau en herstellen geen gegevens op gebruikersniveau.

Bron: www.habr.com

Voeg een reactie