Jak jsme použili Lazy Replication pro zotavení po havárii s PostgreSQL

Jak jsme použili Lazy Replication pro zotavení po havárii s PostgreSQL
Replikace není záloha. Nebo ne? Zde je návod, jak jsme použili odloženou replikaci k zotavení z náhodného smazání zástupců.

Infrastrukturní specialisté Za práci odpovídá GitLab GitLab.com - největší instance GitLabu v přírodě. Se 3 miliony uživatelů a téměř 7 miliony projektů je to jeden z největších open source SaaS webů s dedikovanou architekturou. Bez databázového systému PostgreSQL se infrastruktura GitLab.com daleko nedostane a co děláme pro zajištění odolnosti proti chybám v případě jakýchkoli selhání, kdy může dojít ke ztrátě dat. Je nepravděpodobné, že k takové katastrofě dojde, ale jsme dobře připraveni a zásobeni různými zálohovacími a replikačními mechanismy.

Replikace není prostředkem k zálohování databází (viz níže). Nyní ale uvidíme, jak rychle obnovit omylem smazaná data pomocí líné replikace: on GitLab.com uživatel smazal zástupce pro projekt gitlab-ce a ztracené spojení s požadavky na sloučení a úkoly.

S odloženou replikou jsme obnovili data za pouhých 1,5 hodiny. Podívejte se, jak se to stalo.

Obnova bodu v čase s PostgreSQL

PostgreSQL má vestavěnou funkci, která obnovuje stav databáze do určitého bodu v čase. To se nazývá Point-in-Time Recovery (PITR) a používá stejné mechanismy, které udržují repliku aktuální: počínaje spolehlivým snímkem celého databázového clusteru (základní záloha) aplikujeme řadu změn stavu až do určitého okamžiku.

Abychom tuto funkci využili pro studené zálohování, pravidelně provádíme základní zálohu databáze a ukládáme ji do archivu (archivy GitLab žijí v Cloudové úložiště Google). Změny stavu databáze sledujeme také archivací protokolu pro zápis (zápis dopředu, WAL). A s tímto vším na místě můžeme provést PITR pro obnovu po havárii: počínaje snímkem pořízeným před selháním a aplikací změn z archivu WAL až po selhání.

Co je odložená replikace?

Líná replikace je aplikace změn z WAL se zpožděním. To znamená, že transakce proběhla za hodinu X, ale v replice se objeví se zpožděním d za hodinu X + d.

PostgreSQL má 2 způsoby, jak nastavit repliku fyzické databáze: obnovení zálohy a streamování replikace. Obnovení z archivu, v podstatě funguje jako PITR, ale nepřetržitě: neustále načítáme změny z archivu WAL a aplikujeme je na repliku. A streamovací replikace přímo načítá tok WAL z hostitele nadřazené databáze. Preferujeme obnovu archivu – je jednodušší na správu a má normální výkon, který drží krok s produkčním clusterem.

Jak nastavit zpožděnou obnovu z archivu

Možnosti obnovení popsané v souboru recovery.conf. Příklad:

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 těmito parametry jsme nakonfigurovali odloženou repliku s obnovou zálohy. Zde se používá wal-e extrahovat segmenty WAL (restore_command) z archivu a změny se projeví po osmi hodinách (recovery_min_apply_delay). Replika bude sledovat změny časové osy v archivu, například kvůli selhání clusteru (recovery_target_timeline).

С recovery_min_apply_delay Můžete nastavit streamovanou replikaci se zpožděním, ale existuje zde několik úskalí, která souvisí s replikačními sloty, zpětnou vazbou v pohotovostním režimu a tak dále. Archiv WAL vám umožňuje se jim vyhnout.

Parametr recovery_min_apply_delay se objevil pouze v PostgreSQL 9.3. V předchozích verzích musíte pro odloženou replikaci nakonfigurovat kombinaci funkce správy obnovy (pg_xlog_replay_pause(), pg_xlog_replay_resume()) nebo podržte segmenty WAL v archivu po dobu zpoždění.

Jak to PostgreSQL dělá?

Je zajímavé vidět, jak PostgreSQL implementuje líné zotavení. Pojďme se podívat recoveryApplyDelay(XlogReaderState). Volá se z hlavní opakovací smyčka pro každý záznam z 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;
}

Pointa je, že zpoždění je založeno na fyzickém čase zaznamenaném v časovém razítku potvrzení transakce (xtime). Jak vidíte, zpoždění se týká pouze odevzdání a neovlivňuje ostatní položky – všechny změny se aplikují přímo a odevzdání je zpožděno, takže změny uvidíme až po nakonfigurovaném zpoždění.

Jak používat zpožděnou repliku k obnovení dat

Řekněme, že máme databázový cluster a repliku s osmihodinovým zpožděním ve výrobě. Podívejme se, jak obnovit data pomocí příkladu nechtěným smazáním zástupců.

Když jsme se o problému dozvěděli, my obnova archivu byla pozastavena pro odloženou repliku:

SELECT pg_xlog_replay_pause();

S pauzou jsme neriskovali, že replika požadavek zopakuje DELETE. Užitečná věc, pokud potřebujete čas, abyste vše pochopili.

Jde o to, že odložená replika musí dosáhnout okamžiku před žádostí DELETE. Přibližně jsme znali fyzický čas odstranění. Smazali jsme recovery_min_apply_delay a přidal recovery_target_time в recovery.conf. Takto replika bez zpoždění dosáhne správného okamžiku:

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

S časovými razítky je lepší snížit přebytek, aby nedošlo k chybě. Pravda, čím větší pokles, tím více dat ztrácíme. Opět, pokud žádost zmeškáme DELETE, vše bude znovu smazáno a budete muset začít znovu (nebo dokonce vzít studenou zálohu pro PITR).

Restartovali jsme odloženou instanci Postgres a segmenty WAL se opakovaly až do zadaného času. Pokrok v této fázi můžete sledovat dotazem:

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;

Pokud se časové razítko již nemění, obnova je dokončena. Akci lze přizpůsobit recovery_target_actionzavřít, povýšit nebo pozastavit instanci po opakování (ve výchozím nastavení je pozastavena).

Databáze se vrátila do stavu před tím nešťastným požadavkem. Nyní můžete například exportovat data. Vyexportovali jsme smazaná data štítků a všechny odkazy na problémy a žádosti o sloučení a přesunuli je do produkční databáze. Pokud jsou ztráty velkého rozsahu, můžete repliku jednoduše propagovat a použít ji jako hlavní. Ale pak budou všechny změny po bodu, do kterého jsme se vzpamatovali, ztraceny.

Místo časových razítek je lepší používat ID transakcí. Tato ID je užitečné zaznamenávat například pro příkazy DDL (jako např DROP TABLE), používáním log_statements = 'ddl'. Kdybychom měli ID transakce, vzali bychom recovery_target_xid a spustil vše až k transakci před požadavkem DELETE.

Návrat do práce je velmi jednoduchý: odstraňte všechny změny z recovery.conf a restartujte Postgres. Replika bude mít brzy opět osmihodinové zpoždění a my jsme připraveni na budoucí potíže.

Výhody zotavení

S odloženou replikou namísto studené zálohy nemusíte trávit hodiny obnovováním celého obrazu z archivu. Například získání celé základní 2TB zálohy nám trvá pět hodin. A pak ještě musíte aplikovat celý denní WAL, abyste se dostali do požadovaného stavu (v nejhorším případě).

Odložená replika je lepší než studená záloha dvěma způsoby:

  1. Není potřeba odstraňovat celou základní zálohu z archivu.
  2. Existuje pevné osmihodinové okno segmentů WAL, které se musí opakovat.

Neustále také kontrolujeme, zda je možné udělat PITR z WAL, a sledováním zpoždění odložené repliky bychom rychle zaznamenali poškození nebo jiné problémy s archivem WAL.

V tomto příkladu nám obnovení trvalo 50 minut, což znamená, že rychlost byla 110 GB dat WAL za hodinu (archiv byl stále zapnutý AWS S3). Celkově jsme problém vyřešili a data obnovili za 1,5 hodiny.

Výsledky: kde je odložená replika užitečná (a kde není)

Použijte zpožděnou replikaci jako první pomoc, pokud jste náhodou ztratili data a všimli jste si tohoto problému během nakonfigurovaného zpoždění.

Ale mějte na paměti: replikace není záloha.

Zálohování a replikace mají různé účely. Studená záloha se bude hodit, pokud jste ji omylem vytvořili DELETE nebo DROP TABLE. Uděláme zálohu z chladného úložiště a obnovíme předchozí stav tabulky nebo celé databáze. Ale zároveň žádost DROP TABLE je téměř okamžitě reprodukován ve všech replikách na pracovním clusteru, takže zde běžná replikace nepomůže. Samotná replikace udržuje databázi dostupnou při pronajímání jednotlivých serverů a rozděluje zátěž.

Dokonce i u odložené repliky někdy opravdu potřebujeme chladnou zálohu na bezpečném místě, pokud dojde k selhání datového centra, skrytému poškození nebo jiným událostem, které nejsou okamžitě patrné. Samotná replikace je zde k ničemu.

Poznámka. Na GitLab.com V současnosti chráníme před ztrátou dat pouze na systémové úrovni a neobnovujeme data na uživatelské úrovni.

Zdroj: www.habr.com

Přidat komentář