Cum am folosit replicarea leneșă pentru recuperarea în caz de dezastru cu PostgreSQL

Cum am folosit replicarea leneșă pentru recuperarea în caz de dezastru cu PostgreSQL
Replicarea nu este o copie de rezervă. Sau nu? Iată cum am folosit replicarea leneșă pentru recuperare prin ștergerea accidentală a comenzilor rapide.

specialişti în infrastructură GitLab este responsabil pentru lucru GitLab.com - cea mai mare instanță de GitLab din natură. Cu 3 milioane de utilizatori și aproape 7 milioane de proiecte, este unul dintre cele mai mari site-uri SaaS open source cu o arhitectură dedicată. Fără sistemul de baze de date PostgreSQL, infrastructura GitLab.com nu va merge departe și ceea ce pur și simplu nu facem pentru toleranța la erori în cazul oricăror erori atunci când puteți pierde date. Este puțin probabil să se întâmple o astfel de catastrofă, dar ne-am pregătit bine și ne-am aprovizionat cu diverse mecanisme de rezervă și replicare.

Replicarea nu este instrumentul de backup al bazei de date (Vezi mai jos). Dar acum vom vedea cum să recuperăm rapid datele șterse accidental folosind replicarea leneșă: activat GitLab.com utilizator scurtătură eliminată pentru proiect gitlab-ce și pierderea conexiunilor cu cererile și sarcinile de îmbinare.

Cu replica întârziată, am recuperat datele în doar 1,5 ore. Vezi cum a fost.

Recuperare la un moment dat cu PostgreSQL

PostgreSQL are o funcție încorporată care restabilește starea unei baze de date la un anumit moment în timp. Se numeste Recuperare punct-in-time (PITR) și folosește aceleași mecanisme care mențin o replică la zi: începând cu un instantaneu de încredere al întregului cluster de baze de date (backup de bază), aplicăm o serie de modificări de stare până la un anumit moment în timp.

Pentru a folosi această caracteristică pentru o copie de rezervă la rece, facem în mod regulat o copie de rezervă a bazei de date de bază și o stocăm într-o arhivă (arhivele GitLab trăiesc în Stocare în cloud Google). De asemenea, monitorizăm modificările stării bazei de date prin arhivarea unui jurnal de scriere anticipată (scrie înainte jurnalul, WAL). Și cu toate acestea, putem face PITR pentru recuperare în caz de dezastru: începem cu un instantaneu făcut înainte de eroare și aplicăm modificările din arhiva WAL până la accident.

Ce este replicarea întârziată?

Replicarea întârziată este aplicarea modificărilor de la WAL cu întârziere. Adică tranzacția a avut loc la ora X, dar va apărea în replică cu întârziere d într-o oră X + d.

Există 2 moduri de a configura o replică fizică a bazei de date în PostgreSQL: restaurarea arhivei și replicarea în flux. Restaurare dintr-o arhivă, funcționează în esență ca PITR, dar continuu: extragem constant modificări din arhiva WAL și le aplicăm pe replică. A replicare în flux preia fluxul WAL direct de la gazda bazei de date din amonte. Preferăm restaurarea dintr-o arhivă - este mai ușor de gestionat și are performanță normală, care nu rămâne în urma unui cluster de producție.

Cum să configurați recuperarea de rezervă întârziată

Opțiuni de recuperare descrise în dosar recovery.conf... Exemplu:

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'

Cu aceste setări, am configurat o replică întârziată cu restaurarea arhivei. Folosit aici wal-e pentru a extrage segmente WAL (restore_command) din arhivă, iar modificările vor fi aplicate după opt ore (recovery_min_apply_delay). Replica va urmări modificările cronologiei din arhivă, cum ar fi din cauza unei failovere a clusterului (recovery_target_timeline).

С recovery_min_apply_delay puteți configura replicarea în flux cu latență, dar există câteva capcane asociate cu sloturile de replicare, feedback-ul de rezervă și așa mai departe. Arhiva WAL le evită.

Parametru recovery_min_apply_delay a apărut doar în PostgreSQL 9.3. În versiunile anterioare, replicarea întârziată necesită o combinație de funcții de management al recuperării (pg_xlog_replay_pause(), pg_xlog_replay_resume()) sau păstrați segmentele WAL în arhivă pe durata întârzierii.

Cum face PostgreSQL?

Este interesant de văzut cum PostgreSQL implementează restaurarea leneșă. Să ne uităm la recoveryApplyDelay(XlogReaderState). Se numește de la repetarea buclei principale pentru fiecare intrare din 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;
}

Concluzia este că întârzierea se bazează pe timpul fizic înregistrat în marcajul de timp al comenzii tranzacției (xtime). După cum puteți vedea, întârzierea se aplică numai comitărilor și nu afectează alte înregistrări - toate modificările sunt aplicate direct, iar commit-ul este întârziat, așa că vom vedea modificările numai după întârzierea configurată.

Cum să utilizați replica leneșă pentru recuperarea datelor

Să presupunem că avem un cluster de baze de date în producție și o replică cu o întârziere de opt ore. Să vedem cum să recuperăm datele folosind un exemplu ștergerea accidentală a comenzilor rapide.

Când am devenit conștienți de problemă, noi recuperare de rezervă întreruptă pentru replica întârziată:

SELECT pg_xlog_replay_pause();

Cu o pauză, nu am riscat ca replica să repete cererea DELETE. Lucru util dacă aveți nevoie de timp pentru a înțelege totul.

Concluzia este că replica întârziată trebuie să ajungă în momentul dinaintea solicitării DELETE. Știam aproximativ momentul fizic al îndepărtării. Am scos recovery_min_apply_delay si adaugat recovery_target_time в recovery.conf. Deci replica ajunge la momentul potrivit fără întârziere:

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

Cu marcajele de timp, este mai bine să reduceți excesul pentru a nu rata. Adevărat, cu cât scăderea este mai mare, cu atât pierdem mai multe date. Din nou, dacă sărim peste cerere DELETE, totul va fi șters din nou și va trebui să o luați de la capăt (sau chiar să faceți o copie de rezervă rece pentru PITR).

Am repornit instanța Postgres întârziată și segmentele WAL au fost repetate până la ora specificată. Puteți urmări progresul în această etapă interogând:

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;

Dacă marca temporală nu se mai modifică, restaurarea este completă. Puteți personaliza acțiunea recovery_target_actionpentru a închide, promova sau întrerupe instanța după o reîncercare (se întrerupe în mod implicit).

Baza de date a ajuns în statul înainte de acea cerere nefastă. Acum puteți, de exemplu, să exportați date. Am exportat datele etichetelor de la distanță și toate linkurile către probleme și solicitări de îmbinare și le-am transferat în baza de date de producție. Dacă pierderile sunt mari, poți pur și simplu să promovezi replica și să o folosești ca principală. Dar apoi toate schimbările se vor pierde după momentul în care ne-am revenit.

Este mai bine să utilizați ID-uri de tranzacție în loc de marcaje de timp. Este util să înregistrați aceste ID-uri, de exemplu, pentru instrucțiunile DDL (cum ar fi DROP TABLE), prin utilizarea log_statements = 'ddl'. Dacă am avea un ID de tranzacție, am lua recovery_target_xid și a rulat totul până la tranzacție înainte de solicitare DELETE.

Revenirea la muncă este foarte simplă: eliminați toate modificările din recovery.conf și reporniți postgres. În curând, tacul va avea din nou o întârziere de opt ore și suntem pregătiți pentru probleme viitoare.

Beneficii de recuperare

Cu o replică întârziată, în loc de o copie de rezervă rece, nu trebuie să petreceți ore întregi restaurând întregul instantaneu din arhivă. De exemplu, avem nevoie de cinci ore pentru a obține întreaga copie de rezervă de bază de 2 TB. Și apoi mai trebuie să aplicați întregul WAL zilnic pentru a reveni la starea dorită (în cel mai rău caz).

O replică întârziată este mai bună decât o copie de rezervă rece în două moduri:

  1. Nu trebuie să obțineți întreaga copie de rezervă de bază din arhivă.
  2. Există o fereastră fixă ​​de opt ore de segmente WAL care trebuie repetată.

De asemenea, verificăm în mod constant dacă WAL poate fi PITR și am observa rapid corupție sau alte probleme cu arhiva WAL prin monitorizarea întârzierii replicii întârziate.

În acest exemplu, ne-a luat 50 de minute pentru a restabili, adică viteza a fost de 110 GB de date WAL pe oră (arhiva era încă activată AWS S3). În total, am rezolvat problema și am restaurat datele în 1,5 ore.

Rezumat: unde este utilă o replică întârziată (și unde nu)

Utilizați replicarea întârziată ca prim ajutor dacă pierdeți din greșeală date și observați acest dezastru în termenul de întârziere configurat.

Dar rețineți: replicarea nu este o copie de rezervă.

Backup-ul și replicarea au scopuri diferite. O copie de rezervă rece va fi utilă dacă ați făcut-o accidental DELETE sau DROP TABLE. Facem o copie de rezervă din stocarea la rece și restaurăm starea anterioară a unui tabel sau a unei întregi baze de date. Dar în același timp și cererea DROP TABLE reprodus aproape instantaneu în toate replicile de pe clusterul de lucru, astfel încât replicarea regulată nu se va salva aici. Replicarea în sine menține baza de date disponibilă atunci când serverele individuale sunt închiriate și distribuie încărcarea.

Chiar și cu o copie întârziată, uneori avem nevoie de o copie de rezervă la rece într-un loc sigur, dacă dintr-o dată apare o defecțiune a centrului de date, daune ascunse sau alte evenimente pe care nu le observi imediat. Aici dintr-o replicare nu are sens.

Nota. pe GitLab.com În prezent, protejăm doar împotriva pierderii datelor la nivel de sistem și nu restaurăm datele la nivel de utilizator.

Sursa: www.habr.com

Adauga un comentariu