Hur vi anvÀnde fördröjd replikering för katastrofÄterstÀllning med PostgreSQL

Hur vi anvÀnde fördröjd replikering för katastrofÄterstÀllning med PostgreSQL
Replikering Àr inte en sÀkerhetskopia. Eller Àr den det? SÄ hÀr anvÀnde vi fördröjd replikering för att ÄterstÀlla, efter att ha raderat etiketter av misstag.

Infrastruktur-specialister pĂ„ GitLab ansvarar för driften GitLab.com – den största GitLab-instansen i naturen. HĂ€r finns 3 miljoner anvĂ€ndare och nĂ€stan 7 miljoner projekt, och det Ă€r en av de största open source SaaS-webbplatserna med en dedikerad arkitektur. Utan PostgreSQL-databassystem kommer GitLab.com inte lĂ„ngt, och vi gör vad vi kan för att sĂ€kerstĂ€lla motstĂ„ndskraft mot eventuella fel dĂ€r data kan gĂ„ förlorade. SĂ„dan katastrof Ă€r osannolik, men vi Ă€r vĂ€l förberedda och har olika backup- och replikeringstekniker i beredskap.

Replikering Àr inte en lösning för databasbackup (se nedan). Men nu ska vi se hur snabbt vi kan ÄterstÀlla av misstag raderade data med hjÀlp av fördröjd replikering: nÀr en GitLab.com anvÀndare raderade en etikett för projektet gitlab-ce och förlorade kopplingen till merge-förfrÄgningar och uppgifter.

Med den fördröjda replikationen ÄterstÀllde vi data pÄ bara 1,5 timmar. Se hur det gick till.

ÅterstĂ€llning till en specifik tid med PostgreSQL

PostgreSQL har en inbyggd funktion för att ÄterstÀlla databasens tillstÄnd till en viss tidpunkt. Det kallas Point-in-Time Recovery (PITR) och anvÀnder samma mekanismer som hÄller replikationen aktuell: utifrÄn en pÄlitlig ögonblicksbild av hela databasens kluster (grundbackup) tillÀmpar vi en serie tillstÄndsÀndringar fram till en viss tidpunkt.

För att anvÀnda denna funktion för kall backup gör vi regelbundet en grundbackup av databasen och sparar den i ett arkiv (GitLabs arkiv finns i Google Cloud Storage). Vi övervakar ocksÄ förÀndringar av databasens tillstÄnd genom att arkivera loggen för förhandsinspelning (write-ahead log, WAL). Med allt detta kan vi utföra PITR för katastrofÄterstÀllning: vi börjar med en ögonblicksbild som togs före felet och tillÀmpar förÀndringar frÄn WAL-arkivet fram till kraschen.

Vad Àr fördröjd replikering?

Fördröjd replikering Àr tillÀmpningen av Àndringar frÄn WAL med en fördröjning. Det vill sÀga transaktionen intrÀffade vid tidpunkt X, men i replikatet kommer den att visas med en fördröjning d vid tidpunkt X + d.

I PostgreSQL finns det 2 sĂ€tt att konfigurera en fysisk databasreplik: Ă„terstĂ€llning frĂ„n arkiv och strömreplikering. ÅterstĂ€llning frĂ„n arkiv, fungerar i grunden som PITR, men kontinuerligt: vi extraherar stĂ€ndigt Ă€ndringar frĂ„n WAL-arkivet och tillĂ€mpar dem pĂ„ repliket. Och strömreplikering extraherar direkt WAL-strömmen frĂ„n den överordnade databashostens. Vi föredrar Ă„terstĂ€llning frĂ„n arkiv - det Ă€r enklare att hantera och har en normal prestation som inte ligger efter den aktiva klustern.

Hur man konfigurerar fördröjd ÄterstÀllning frÄn arkiv

ÅterstĂ€llningsalternativ beskrivs i filen recovery.conf. Exempel:

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'

Med dessa instÀllningar har vi konfigurerat en fördröjd replik med ÄterstÀllning frÄn arkiv. HÀr anvÀnds wal-e för att extrahera WAL-segment (restore_command) frÄn arkivet, och Àndringarna kommer att tillÀmpas efter Ätta timmar (recovery_min_apply_delay). Repliket kommer att övervaka tidslinjens Àndringar i arkivet, till exempel pÄ grund av ett felövergÄng i klustret (recovery_target_timeline).

Med recovery_min_apply_delay kan man konfigurera strömreplikering med fördröjning, men hÀr finns ett par fÀllor kopplade till replikationsslotten, Äterkopplingen av het reserv och liknande. WAL-arkiv möjliggör att undvika dem.

Parameter recovery_min_apply_delay kom endast i PostgreSQL 9.3. I tidigare versioner var det nödvÀndigt att konfigurera en kombination av ÄterstÀllningshanteringsfunktioner (pg_xlog_replay_pause(), pg_xlog_replay_resume()) eller att hÄlla WAL-segment i arkivet under fördröjningstiden.

Hur gör PostgreSQL detta?

Det Àr intressant att se hur PostgreSQL implementerar fördröjd ÄterstÀllning. LÄt oss titta pÄ recoveryApplyDelay(XlogReaderState). Den kallas frÄn huvudÄterspelningscykeln för varje post frÄn WAL.

static bool
recoveryApplyDelay(XLogReaderState *record)
{
    uint8       xact_info;
    TimestampTz xtime;
    long        secs;
    int         microsecs;

    /* inget inget om ingen fördröjning konfigurerad */
    if (recovery_min_apply_delay <= 0)
        return false;

    /* ingen fördröjning tillÀmpas pÄ en databas som inte Ànnu Àr konsekvent */
    if (!reachedConsistency)
        return false;

    /**
     * Är det en COMMIT-post?
     *
     * Vi vÀljer medvetet att inte fördröja aborter eftersom de inte har nÄgon effekt pÄ
     * MVCC. Vi tillÄter redan uppspelning av poster som inte har en tidsstÀmpel,
     * sÄ det finns redan möjlighet för problem orsakade av tidiga konflikter pÄ
     * standby.
     */
    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);

    /**
     * Avsluta utan att aktivera latch om det redan har passerat tid att tillÀmpa denna
     * post
     */
    TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
                        &secs, &microsecs);
    if (secs <= 0 && microsecs <= 0)
        return false;

    while (true)
    {
        // Förkortat:
        // AnvÀnd WaitLatch tills vi nÄr recoveryDelayUntilTime
        // och sedan
        break;
    }
    return true;
}

PoÀngen Àr att fördröjningen baseras pÄ den fysiska tiden som finns i tidsstÀmpeln för transaktionskommittén (xtime). Som framgÄr tillÀmpas fördröjningen endast pÄ kommittéer och berör inte andra poster - alla Àndringar tillÀmpas direkt, medan kommittén fördröjs, sÄ vi kommer att se Àndringarna först efter den instÀllda fördröjningen.

Hur man anvÀnder en fördröjd replik för datagenerering

Anta att vi har en databas i produktion och en replik med Ätta timmars fördröjning. LÄt oss se hur vi kan ÄterstÀlla data med exemplet slumparbete borttagning av etiketter.

NÀr vi fick veta problemet stannade vi ÄterstÀllningen frÄn arkivet för den fördröjda replikationen:

SELECT pg_xlog_replay_pause();

Med en paus riskerade vi inte att replikationen upprepade förfrÄgan. TA BORTEn anvÀndbar sak att ha tid pÄ oss att lösa allt.

HuvudpoÀngen Àr att den fördröjda replikationen mÄste nÄ fram till punkt innan förfrÄgan. TA BORTVi visste ungefÀr den fysiska tiden för borttagningen. Vi tog bort recovery_min_apply_delay och lade till recovery_target_time pÄ recovery.conf. PÄ sÄ sÀtt nÄr replikeringen den önskade tidspunkten utan fördröjningar:

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

Med tidsstĂ€mplar Ă€r det bĂ€st att dra av lite extra sĂ„ att vi inte missar. Sanningen Ă€r att ju mer vi drar av, desto mer data gĂ„r förlorat. Återigen, om vi missar förfrĂ„gan, TA BORT, allt tas bort igen och vi fĂ„r börja om (eller till och med ta en kall backup för PITR).

Vi har startat om den avbrutna instansen av Postgres och WAL-segmenten upprepades fram till den angivna tiden. Du kan spÄra framgÄngen i detta skede med följande frÄga:

SELECT
  -- nuvarande plats i WAL
  pg_last_xlog_replay_location(),
  -- nuvarande tidsstÀmpel för transaktionen (status för replikatet)
  pg_last_xact_replay_timestamp(),
  -- nuvarande fysiska tid
  now(),
  -- mÀngden tid som fortfarande mÄste tillÀmpas tills recovery_target_time har uppnÄtts
  '2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;

Om tidsstÀmpeln inte lÀngre Àndras Àr ÄterstÀllningen slutförd. Du kan konfigurera ÄtgÀrden recovery_target_action, för att stÀnga, flytta fram eller pausa instansen efter upprepningen (som standard pausas den).

Databasen har ÄtergÄtt till tillstÄndet före den olyckliga förfrÄgan. Nu kan du till exempel exportera data. Vi exporterade de borttagna data om etiketten och alla relationer med uppgifter och merge-förfrÄgningar och flyttade dem till produktionsdatabasen. Om förlusterna Àr omfattande kan man bara flytta fram replikatet och anvÀnda det som grunddata. Men dÄ kommer alla Àndringar efter den tidpunkt vi ÄterstÀllde att gÄ förlorade.

IstÀllet för tidsstÀmplar Àr det bÀttre att anvÀnda transaktions-ID. Det Àr bra att registrera dessa ID, till exempel för DDL-operatörer (typ DROP TABLE), med hjÀlp av log_statements = 'ddl'. Om vi hade haft ett transaktions-ID skulle vi ha tagit recovery_target_xid och kört allt fram till transaktionen före förfrÄgan. TA BORT.

Att ÄtergÄ till arbetet Àr vÀldigt enkelt: ta bort alla Àndringar frÄn recovery.conf och starta om Postgres. Snart kommer det igen att finnas en Ättatimmarsfördröjning i replikatet, och vi Àr redo för framtida problem.

Fördelar med ÄterstÀllning

Med en fördröjd replik behöver man inte ÄterstÀlla hela bilden frÄn arkivet i timmar. Vi behöver till exempel fem timmar för att hÀmta hela grundbackupen pÄ 2 TB. Och sen mÄste vi dessutom tillÀmpa hela den dagliga WAL:n för att ÄterstÀlla till önskat tillstÄnd (i vÀrsta fall).

En fördröjd replik Àr bÀttre Àn en kall backup pÄ tvÄ punkter:

  1. Det finns inget behov av att hÀmta hela grundbackupen frÄn arkivet.
  2. Det finns ett fast Ättatimmarsfönster för WAL-segment som mÄste upprepas.

Dessutom kontrollerar vi kontinuerligt om det gÄr att göra PITR frÄn WAL, och vi skulle snabbt mÀrka skador eller andra problem med WAL-arkivet genom att övervaka fördröjningen i replikatet.

I det hÀr exemplet tog det oss 50 minuter att ÄterstÀlla, vilket innebÀr att hastigheten var 110 GB WAL-data per timme (arkivet var fortfarande pÄ AWS S3). Totalt löste vi problemet och ÄterstÀllde data pÄ 1,5 timmar.

Sammanfattning: var avsatt replikation Àr anvÀndbar (och var den inte Àr det)

AnvÀnd avsatt replikation som en första hjÀlpen-lösning om du oavsiktligt har förlorat data och mÀrkt detta inom den instÀllda fördröjningen.

Men kom ihÄg: replikation Àr ingen sÀkerhetskopia.

SÀkerhetskopiering och replikation har olika syften. En kall sÀkerhetskopia Àr anvÀndbar om du oavsiktligt har gjort TA BORT eller DROP TABLE. Vi gör en sÀkerhetskopia frÄn det kalla lagret och ÄterstÀller det tidigare tillstÄndet för tabellen eller hela databasen. Men under tiden frÄgan DROP TABLE Äterskapas nÀstan omedelbart i alla repliker pÄ arbetsklustret, sÄ vanlig replikation hjÀlper inte hÀr. SjÀlva replikationen hÄller databasen tillgÀnglig nÀr vissa servrar gÄr ner och fördelar belastningen.

Även med avsatt replikation behöver vi ibland en kall sĂ€kerhetskopia pĂ„ en sĂ€ker plats om vi plötsligt skulle fĂ„ problem i datacentret, dolda skador eller andra hĂ€ndelser som inte mĂ€rks direkt. HĂ€r Ă€r det ingen nytta av enbart replikation.

AnmÀrkning. Vid GitLab.com skyddar vi just nu endast pÄ systemnivÄ mot dataförlust och ÄterstÀller inte data pÄ anvÀndarnivÄ.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster