
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.
pĂ„ GitLab ansvarar för driften â 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 (). 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 anvÀndare för projektet 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 (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 ). Vi övervakar ocksÄ förÀndringar av databasens tillstÄnd genom att arkivera loggen för förhandsinspelning (, 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. , fungerar i grunden som PITR, men kontinuerligt: vi extraherar stÀndigt Àndringar frÄn WAL-arkivet och tillÀmpar dem pÄ repliket. Och 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
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 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 (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Ä . Den kallas frÄn 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, µsecs);
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 .
NÀr vi fick veta problemet stannade vi 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 , 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:
- Det finns inget behov av att hÀmta hela grundbackupen frÄn arkivet.
- 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Ä ). 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 skyddar vi just nu endast pÄ systemnivÄ mot dataförlust och ÄterstÀller inte data pÄ anvÀndarnivÄ.
KĂ€lla: habr.com
