Kako smo koristili Lazy Replication za oporavak od katastrofe sa PostgreSQL

Kako smo koristili Lazy Replication za oporavak od katastrofe sa PostgreSQL
Replikacija nije sigurnosna kopija. Ili ne? Evo kako smo koristili odgođenu replikaciju za oporavak od slučajnog brisanja prečica.

Stručnjaci za infrastrukturu GitLab je odgovoran za rad GitLab.com - najveća GitLab instanca u prirodi. Sa 3 miliona korisnika i skoro 7 miliona projekata, jedan je od najvećih open source SaaS sajtova sa namenskom arhitekturom. Bez PostgreSQL sistema baze podataka, GitLab.com infrastruktura neće dogurati daleko, a šta radimo da osiguramo toleranciju grešaka u slučaju bilo kakvih kvarova kada podaci mogu biti izgubljeni. Malo je vjerovatno da će se takva katastrofa dogoditi, ali mi smo dobro pripremljeni i opskrbljeni raznim mehanizmima sigurnosne kopije i replikacije.

Replikacija nije sredstvo pravljenja sigurnosne kopije baza podataka (vidi ispod). Ali sada ćemo vidjeti kako brzo oporaviti slučajno izbrisane podatke koristeći lijenu replikaciju: uključeno GitLab.com korisnik obrisao prečicu za projekat gitlab-ce i izgubljene veze sa zahtjevima za spajanje i zadacima.

Uz odgođenu repliku, oporavili smo podatke za samo 1,5 sat. Pogledaj kako se to dogodilo.

Oporavak u trenutku uz PostgreSQL

PostgreSQL ima ugrađenu funkciju koja vraća stanje baze podataka na određeni trenutak u vremenu. To se zove Point-in-Time Recovery (PITR) i koristi iste mehanizme koji održavaju repliku ažurnom: počevši od pouzdanog snimka cijelog klastera baze podataka (bazna sigurnosna kopija), primjenjujemo niz promjena stanja do određenog trenutka.

Da bismo koristili ovu funkciju za hladno sigurnosno kopiranje, redovno pravimo osnovnu sigurnosnu kopiju baze podataka i pohranjujemo je u arhivu (GitLab arhive žive u Google pohrana u oblaku). Također pratimo promjene u stanju baze podataka arhiviranjem dnevnika zapisa unaprijed (zapisnik unaprijed, WAL). A sa svim ovim na mjestu, možemo napraviti PITR za oporavak od katastrofe: počevši od snimka snimljenog prije kvara i primjene promjena iz WAL arhive do neuspjeha.

Šta je odgođena replikacija?

Lijena replikacija je primjena promjena iz WAL-a sa zakašnjenjem. To jest, transakcija se dogodila za sat vremena X, ali će se pojaviti u replici sa zakašnjenjem d Za sat vremena X + d.

PostgreSQL ima 2 načina za postavljanje replike fizičke baze podataka: oporavak sigurnosne kopije i replikacija strujanja. Vraćanje iz arhive, u suštini radi kao PITR, ali kontinuirano: stalno preuzimamo promjene iz WAL arhive i primjenjujemo ih na repliku. A streaming replikacija direktno preuzima WAL tok iz uzvodnog hosta baze podataka. Preferiramo oporavak arhive - njime je lakše upravljati i ima normalne performanse koje prati proizvodni klaster.

Kako postaviti odloženi oporavak iz arhive

Opcije oporavka opisano u fajlu 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'

Sa ovim parametrima smo konfigurisali odgođenu repliku sa obnavljanjem rezervne kopije. Ovdje se koristi wal-e za izdvajanje WAL segmenata (restore_command) iz arhive, a promjene će biti primijenjene nakon osam sati (recovery_min_apply_delay). Replika će pratiti promjene vremenske trake u arhivi, na primjer zbog klastera (recovery_target_timeline).

С recovery_min_apply_delay Možete postaviti replikaciju strujanja sa zakašnjenjem, ali ovdje postoji nekoliko zamki koje se odnose na slotove za replikaciju, povratne informacije o vrućoj pripravnosti i tako dalje. WAL arhiva vam omogućava da ih izbjegnete.

Parametar recovery_min_apply_delay pojavio se samo u PostgreSQL 9.3. U prethodnim verzijama, za odgođenu replikaciju trebate konfigurirati kombinaciju funkcije upravljanja oporavkom (pg_xlog_replay_pause(), pg_xlog_replay_resume()) ili zadržati WAL segmente u arhivi za vrijeme trajanja kašnjenja.

Kako PostgreSQL to radi?

Zanimljivo je vidjeti kako PostgreSQL implementira lijeni oporavak. Hajde da pogledamo recoveryApplyDelay(XlogReaderState). Poziva se iz glavna petlja ponavljanja za svaki unos iz WAL-a.

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

Suština je da je kašnjenje zasnovano na fizičkom vremenu zabilježenom u vremenskoj oznaci polaganja transakcije (xtime). Kao što vidite, kašnjenje se odnosi samo na urezivanje i ne utiče na druge unose - sve promene se primenjuju direktno, a urezivanje je odloženo, tako da ćemo promene videti tek nakon konfigurisanog kašnjenja.

Kako koristiti odloženu repliku za vraćanje podataka

Recimo da imamo klaster baze podataka i repliku sa osmosatnim kašnjenjem u proizvodnji. Pogledajmo kako oporaviti podatke koristeći primjer slučajno brisanje prečica.

Kada smo saznali za problem, mi obnavljanje arhive je pauzirano za odgođenu repliku:

SELECT pg_xlog_replay_pause();

Uz pauzu, nismo imali rizik da će replika ponoviti zahtjev DELETE. Korisna stvar ako vam treba vremena da sve shvatite.

Poenta je da odgođena replika mora doći do trenutka prije zahtjeva DELETE. Približno smo znali fizičko vrijeme uklanjanja. Izbrisali smo recovery_min_apply_delay i dodao recovery_target_time в recovery.conf. Ovako replika stiže u pravi trenutak bez odlaganja:

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

Sa vremenskim oznakama, bolje je smanjiti višak kako ne biste propustili. Istina, što je veći pad, gubimo više podataka. Opet, ako propustimo zahtjev DELETE, sve će biti ponovo izbrisano i morat ćete početi ispočetka (ili čak napraviti hladnu sigurnosnu kopiju za PITR).

Ponovo smo pokrenuli odloženu Postgres instancu i WAL segmenti su se ponavljali do određenog vremena. Možete pratiti napredak u ovoj fazi tako što ćete pitati:

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;

Ako se vremenska oznaka više ne mijenja, oporavak je završen. Akcija se može prilagoditi recovery_target_actionda zatvorite, unapredite ili pauzirate instancu nakon ponovnog pokušaja (podrazumevano je suspendovana).

Baza podataka se vratila u stanje prije tog nesretnog zahtjeva. Sada možete, na primjer, izvesti podatke. Izvezli smo podatke o obrisanoj etiketi i sve veze do problema i zahtjeva za spajanjem i premjestili ih u proizvodnu bazu podataka. Ako su gubici velikih razmjera, možete jednostavno promovirati repliku i koristiti je kao glavnu. Ali tada će sve promjene nakon tačke do koje smo se oporavili biti izgubljene.

Umjesto vremenskih oznaka, bolje je koristiti ID transakcije. Korisno je zabilježiti ove ID-ove, na primjer, za DDL naredbe (kao npr DROP TABLE), korištenjem log_statements = 'ddl'. Da imamo ID transakcije, uzeli bismo recovery_target_xid i pokrenuo sve do transakcije prije zahtjeva DELETE.

Povratak na posao je vrlo jednostavan: uklonite sve promjene iz recovery.conf i ponovo pokrenite Postgres. Replika će uskoro ponovo imati osam sati kašnjenja, a mi smo spremni za buduće probleme.

Prednosti oporavka

Uz odgođenu repliku umjesto hladne sigurnosne kopije, ne morate trošiti sate vraćajući cijelu sliku iz arhive. Na primjer, potrebno nam je pet sati da dobijemo cijelu osnovnu sigurnosnu kopiju od 2 TB. I onda još uvijek morate primijeniti cijeli dnevni WAL da biste se oporavili u željeno stanje (u najgorem slučaju).

Odgođena replika je bolja od hladne sigurnosne kopije na dva načina:

  1. Nema potrebe za uklanjanjem cijele osnovne sigurnosne kopije iz arhive.
  2. Postoji fiksni osmočasovni prozor WAL segmenata koji se moraju ponoviti.

Također stalno provjeravamo da li je moguće napraviti PITR iz WAL-a, a brzo bismo uočili oštećenje ili druge probleme sa WAL arhivom praćenjem kašnjenja odgođene replike.

U ovom primjeru, trebalo nam je 50 minuta za vraćanje, što znači da je brzina bila 110 GB WAL podataka na sat (arhiva je još uvijek bila uključena AWS S3). Ukupno smo riješili problem i povratili podatke za 1,5 sat.

Rezultati: gdje je odgođena replika korisna (a gdje nije)

Koristite odgođenu replikaciju kao prvu pomoć ako ste slučajno izgubili podatke i primijetili ovaj problem unutar konfiguriranog kašnjenja.

Ali imajte na umu: replikacija nije rezervna kopija.

Sigurnosna kopija i replikacija imaju različite svrhe. Hladna rezervna kopija će vam dobro doći ako ste slučajno napravili DELETE ili DROP TABLE. Izrađujemo rezervnu kopiju iz hladnog skladišta i vraćamo prethodno stanje tablice ili cijele baze podataka. Ali istovremeno i zahtjev DROP TABLE se gotovo trenutno reproducira u svim replikama na radnom klasteru, tako da obična replikacija ovdje neće pomoći. Sama replikacija održava bazu podataka dostupnom kada su pojedinačni serveri iznajmljeni i raspoređuje opterećenje.

Čak i sa odgođenom replikom, ponekad nam je zaista potrebna hladna sigurnosna kopija na sigurnom mjestu ako dođe do kvara podatkovnog centra, skrivenih oštećenja ili drugih događaja koji nisu odmah uočljivi. Sama replikacija ovdje nije od koristi.

primjedba... Uključeno GitLab.com Trenutno štitimo samo od gubitka podataka na nivou sistema i ne obnavljamo podatke na nivou korisnika.

izvor: www.habr.com

Dodajte komentar