Kako smo koristili odloženu replicaciju za oporavak od katastrofe s PostgreSQL

Kako smo koristili odloženu replicaciju za oporavak od katastrofe s PostgreSQL
Replikacija nije backup. Ili možda jeste? Evo kako smo koristili odloženu replikaciju za oporavak nakon što smo slučajno obrisali prečace.

Stručnjaci za infrastrukturu na GitLabu odgovaraju za rad GitLab.com – najvećeg GitLab instance ikada. Ovdje se nalazi 3 miliona korisnika i gotovo 7 miliona projekata, i to je jedan od najvećih open-source SaaS sa specijaliziranom arhitekturom. Bez sustava baza podataka PostgreSQL, infrastruktura GitLab.com ne bi daleko dogurala, a sve što radimo za otpornost na kvarove kada možemo izgubiti podatke. Vjerojatno se takva katastrofa neće dogoditi, ali smo se dobro pripremili i opskrbili raznim mehanizmima backup-a i replikacije.

Replikacija nije način za backup baza podataka (vidi dolje). No, sada ćemo vidjeti kako brzo obnoviti slučajno obrisane podatke pomoću odložene replikacije: na GitLab.com korisnik izbrisao prečac za projekt gitlab-ce i izgubio vezu s merge requestovima i zadacima.

Sa odloženom replikom obnovili smo podatke u svega 1,5 sata. Pogledajte kako je to bilo.

Obnova na određeno vrijeme s PostgreSQL

PostgreSQL ima ugrađenu funkciju koja obnavlja stanje baze podataka na određeni trenutak. Zove se Point-in-Time Recovery (PITR) i koristi iste mehanizme koji podržavaju ažurnost replike: započinjemo s pouzdanim snimkom cijelog klastera baza podataka (osnovni backup), a zatim primjenjujemo niz promjena stanja do određenog trenutka.

Kako bismo koristili ovu funkciju za hladni backup, redovito radimo osnovni backup baze podataka i pohranjujemo ga u arhivu (arhive GitLab-a žive u Google Cloud Storage). Osim toga, pratimo promjene stanja baze podataka, arhivirajući zapisni dnevnik (write-ahead log, WAL). I sa svime ovim možemo izvesti PITR za hitnu obnovu: počinjemo od snimka napravljenog prije greške i primjenjujemo promjene iz WAL arhive do kvara.

Što je odložena replikacija?

Odložena replikacija je primjena promjena iz WAL-a s odgodom. Drugim riječima, transakcija se dogodila u satu X, ali u replici će se pojaviti s odgodom d za sat X + d.

U PostgreSQL-u postoje 2 načina za postavljanje fizičke replike baze podataka: obnova iz arhive i streaming replikacija. Obnova iz arhive, u biti, radi kao PITR, ali neprekidno: stalno izvlačimo promjene iz WAL arhive i primjenjujemo ih na repliku. A streaming replikacija direktno izvlači WAL tok s višeg hosta baze podataka. Preferiramo obnavljanje iz arhive - njima je lakše upravljati i imaju normalne performanse, koje ne zaostaju za radnim klastra.

Kako postaviti odgođeno obnavljanje iz arhive

Opcije obnove opisane su u datoteci recovery.conf. Primjer:

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'

S ovim parametrima postavili smo odgođenu repliku s obnavljanjem iz arhive. Ovdje se koristi wal-e za vađenje WAL segmenata (restore_command) iz arhive, a promjene će se primjenjivati nakon osam sati (recovery_min_apply_delay). Replika će pratiti promjene vremenskog okvira u arhivi, na primjer, zbog obrade otkaza u klasteru (recovery_target_timeline).

S recovery_min_apply_delay može se postaviti streaming replikacija sa zakašnjenjem, ali ovdje postoje neki trikovi koji se odnose na replikažne slotove, povratne informacije s vrućih rezervi itd. WAL arhiva omogućuje izbjegavanje tih problema.

Parametar recovery_min_apply_delay pojavio se samo u PostgreSQL 9.3. U prethodnim verzijama za odgođenu replikaciju trebate postaviti kombinaciju funkcija upravljanja oporavkom (pg_xlog_replay_pause(), pg_xlog_replay_resume()) ili zadržati WAL segmente u arhivi tijekom zadrške.

Kako to PostgreSQL radi?

Zanimljivo je vidjeti kako PostgreSQL implementira odgođeno obnavljanje. Pogledajmo na recoveryApplyDelay(XlogReaderState). Poziva se iz glavnog kruga ponavljanja za svaki zapis iz 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;
}

Bitno je da je odgoda temeljena na fizičkom vremenu zabilježenom u vremenskoj oznaci komita transakcije (xtime). Kao što je vidljivo, odgoda se primjenjuje samo na komite i ne dotiče druge zapise — sve promjene se primjenjuju izravno, a komit se odgađa, tako da ćemo vidjeti promjene tek nakon postavljene odgode.

Kako koristiti odgođenu replicu za oporavak podataka

Pretpostavimo da imamo klaster baze podataka u produkciji i replicu s osmosatnom odgodom. Pogledajmo kako oporaviti podatke na primjeru nasumičnog brisanja oznaka.

Kada smo saznali za problem, zaustavili smo obnovu iz arhiva za odgođenu replicu:

SELECT pg_xlog_replay_pause();

S pauzom nismo imali rizika da će replika ponoviti zahtjev DELETE. Korisna stvar, ako treba vremena da se sve razjasni.

Bitno je da odgođena replika dođe do trenutka prije zahtjeva. DELETEOtprilike smo znali fizičko vrijeme brisanja. Izbrisali smo recovery_min_apply_delay i dodali recovery_target_time u recovery.conf. Tako replika dolazi do potrebnog trenutka bez odgoda:

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

S vremenskim oznakama bolje je smanjiti suvišno kako bismo izbjegli promašaj. Istina, što više smanjujemo, to više podataka gubimo. Opet, ako prokockamo zahtjev, DELETEsve će ponovno biti obrisano i morat ćemo početi iznova (ili uopće uzeti hladnu sigurnosnu kopiju za PITR).

Ponovo smo pokrenuli odloženi Postgres instancu, a WAL segmenti su se ponavljali do navedenog vremena. Napredak u ovoj fazi može se pratiti upitom:

SELECT
  -- trenutna lokacija u WAL
  pg_last_xlog_replay_location(),
  -- trenutni vremenski pečat transakcije (stanje replike)
  pg_last_xact_replay_timestamp(),
  -- trenutno fizičko vrijeme
  now(),
  -- količina vremena koja još treba biti primenjena do postizanja recovery_target_time
  '2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;

Ako se vremenska oznaka više ne menja, obnova je završena. Možete postaviti akciju recovery_target_action, da se zatvori, unapredi ili pauzira instanca nakon ponavljanja (po defaultu se pauzira).

Baza podataka se vratila u stanje pre tog nesretnog upita. Sada je moguće, na primer, izvesti podatke. Izvezli smo uklonjene podatke o oznakama i sve veze s zadacima i merge zahtevima i preneli ih u radnu bazu podataka. Ako su gubici veliki, možemo jednostavno unaprediti replikaciju i koristiti je kao glavnu. Ali tada će svi podaci nakon trenutka do kojeg smo se vratili biti izgubljeni.

Umesto vremenskih oznaka, bolje je koristiti ID transakcija. Korisno je zabeležiti ove ID, na primer, za DDL operatore (tipa DROP TABLE), pomoću log_statements = 'ddl'. Da smo imali ID transakcije, uzeli bismo recovery_target_xid i izvršili sve do transakcije pre upita DELETE.

Povratak na rad je vrlo jednostavan: uklonite sve promene iz recovery.conf i ponovo pokrenite Postgres. Ubrzo će se na replici ponovo pojaviti osmočasovno kašnjenje, i spremni smo za buduće nevolje.

Prednosti za oporavak

Sa odloženom replikom umesto hladnog backupa ne treba nam sate za obnavljanje celog snimka iz arhive. Na primer, treba nam pet sati da izvučemo celi osnovni backup od 2 TB. A zatim ćemo još morati primeniti celu dnevnu WAL, da bismo se oporavili do potrebnog stanja (u najgorem slučaju).

Odložena replika je bolja od hladnog backupa iz dva razloga:

  1. Nije potrebno izvlačiti ceo osnovni backup iz arhive.
  2. Postoji fiksno osmočasovno okno WAL segmenata koje treba ponoviti.

Osim toga, stalno proveravamo može li se PITR obaviti iz WAL, i brzo bismo primetili oštećenja ili druge probleme s arhivom WAL, prateći kašnjenje odložene replike.

U ovom primeru je trebalo 50 minuta za oporavak, što znači da je brzina bila 110 GB podataka WAL po satu (arhiv je tada i dalje bio na AWS S3). Sveukupno smo riješili problem i povratili podatke u 1,5 sati.

Zaključci: gdje će se koristiti odgođena replikacija (a gdje ne)

Koristite odgođenu replikaciju kao sredstvo prve pomoći ako slučajno izgubite podatke i primijetite ovu nesreću unutar postavljene kašnjenja.

Ali imajte na umu: replikacija nije backup.

Backup i replikacija imaju različite ciljeve. Hladni backup će biti koristan ako ste slučajno napravili DELETE ili DROP TABLE. Radimo backup iz hladnog skladišta i obnavljamo prethodno stanje tablice ili cijele baze podataka. Međutim, upit DROP TABLE se gotovo trenutno reproducira na svim replikama u radnom klasteru, tako da će uobičajena replikacija ovdje pomoći. Sama po sebi replikacija održava bazu podataka dostupnom kada pojedini serveri otkazuju i raspoređuje opterećenje.

Čak i s odgođenom replikom, ponekad jako trebamo hladni backup na sigurnom mjestu ako se dogodi kvar datacentra, skriveno oštećenje ili drugi događaji koje odmah ne primijetite. Ovdje od same replikacije nema koristi.

Napomena. Na GitLab.com sada štitimo od gubitka podataka samo na razini sustava i ne obnavljamo podatke na razini korisnika.

Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster