
Replikace není záloha. Nebo ano? Tady je náš způsob, jak jsme použili asynchronní replikaci pro obnovení po náhodném smazání zkratek.
na GitLabu jsou zodpovědní za provoz — největší instance GitLabu na světě. Zde je 3 miliony uživatelů a téměř 7 milionů projektů, což z něj činí jeden z největších open-source SaaS webů s dedikovanou architekturou. Bez databázového systému PostgreSQL by GitLab.com daleko nedošel, a děláme vše pro to, abychom zajistili odolnost proti jakýmkoliv selháním, kdy by mohly být ztraceny údaje. Taková katastrofa pravděpodobně nenastane, ale jsme dobře připraveni a máme různé mechanismy zálohování a replikace.
Replikace, to není zálohování databází (). Ale nyní uvidíme, jak rychle obnovit náhodně smazaná data pomocí asynchronní replikace: na uživateli pro projekt a ztratil vazbu na merge requesty a úkoly.
S asynchronní replikací jsme obnovili data za pouhé 1,5 hodiny. Podívejte se, jak to bylo.
Obnovení do bodu v čase s PostgreSQL
PostgreSQL má vestavěnou funkci, která obnovuje stav databáze do určitého bodu v čase. Říká se jí (PITR) a používá stejné mechanismy, které zajišťují aktuálnost repliky: začínáme důvěryhodným snímkem celého databázového klastru (základní záloha) a aplikujeme sérii změn stavu až do určitého okamžiku.
Abychom mohli tuto funkci použít pro studenou zálohu, pravidelně provádíme základní zálohu databáze a uchováváme ji v archivu (archivy GitLabu žijí v ). Také sledujeme změny stavu databáze archivováním protokolu předběžného zápisu (, WAL). S tímto vším můžeme provést PITR pro obnovu po havárii: začínáme se snímkem před chybou a aplikujeme změny z archivu WAL až do selhání.
Co je to asynchronní replikace?
Asynchronní replikace je aplikace změn z WAL se zpožděním. To znamená, že transakce se stala v hodině X, ale v replikaci se objeví se zpožděním – perioda anténní mřížky (vzdálenost mezi sousedními prvky), o hodinu X + d.
V PostgreSQL existují 2 způsoby, jak nastavit fyzickou replikaci databáze: obnovu z archivu a streamovací replikaci. , v podstatě funguje jako PITR, ale neustále: neustále extrahujeme změny z archivu WAL a aplikujeme je na replikaci. A přímo extrahuje tok WAL z nadřazeného hostitele databáze. Preferujeme obnovu z archivu — je snazší s ní manipulovat a má normální výkon, který není pozadu za pracovním clusterem.
Jak nastavit zpožděnou obnovu z archivu
jsou popsány 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 nastavili zpožděnou replikaci s obnovou z archivu. Používá se zde k extrakci segmentů WAL (restore_command) z archivu, a změny budou aplikovány po osmi hodinách (recovery_min_apply_delay). Replika bude sledovat změny časové osy v archivu, například kvůli vyřazení v clusteru (recovery_target_timeline).
S recovery_min_apply_delay je možné nastavit streamovací replikaci se zpožděním, ale zde jsou některé nástrahy spojené se sloty replikace, zpětnou vazbou horké rezervy a dalšími. Archiv WAL umožňuje jejich vyhnutí.
Parameter recovery_min_apply_delay se objevil pouze v PostgreSQL 9.3. V předchozích verzích bylo nutné nastavit kombinaci (pg_xlog_replay_pause(), pg_xlog_replay_resume()) nebo uchovávat segmenty WAL v archivu po dobu zpoždění.
Jak PostgreSQL to dělá?
Zajímavé je podívat se, jak PostgreSQL realizuje zpožděnou obnovu. Podívejme se na . Je volána z pro každou položku z WAL.
static bool
recoveryApplyDelay(XLogReaderState *record)
{
uint8 xact_info;
TimestampTz xtime;
long secs;
int microsecs;
/* nic se neděje, pokud není nastavena žádná prodleva */
if (recovery_min_apply_delay <= 0)
return false;
/* žádná prodleva se nepoužívá na databázi, která ještě není konzistentní */
if (!reachedConsistency)
return false;
/*
* Je to záznam COMMIT?
*
* Záměrně se rozhodujeme nezdržovat zrušení, protože nemá vliv na
* MVCC. Už nyní umožňujeme přehrávání záznamů, které nemají časové razítko,
* takže už je zde příležitost k problémům způsobeným předčasnými konflikty na
* zálohách.
* /
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);
/*
* Ukončete bez spuštění latch, pokud už je po čase použít tento
* záznam
* /
TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
&secs, µsecs);
if (secs <= 0 && microsecs <= 0)
return false;
while (true)
{
// Zkráceno:
// Použijte WaitLatch, dokud nedosáhneme recoveryDelayUntilTime
// a pak
break;
}
return true;
}Již na tom záleží, protože prodleva je založena na fyzickém čase zaznamenaném v časovém razítku commitu transakce (xtime). Jak je vidět, prodleva se aplikuje pouze na commity a nedotýká se ostatních záznamů — všechny změny se aplikují přímo, a commit se oddaluje, takže vidíme změny až po nastavené prodlevě.
Jak používat odloženou replikaci pro obnovení dat
Představme si, že máme v produkci cluster databáze a replikaci s osmihodinovou prodlevou. Podívejme se, jak obnovit data na příkladu .
Když jsme se o problému dozvěděli, my pro odloženou replikaci:
SELECT pg_xlog_replay_pause();S pauzou jsme neměli riziko, že by replikace zopakovala příkaz DELETE. Užitečná funkce, pokud je potřeba si vše v klidu promyslet.
Již na tom záleží, protože odložená replikace by měla dosáhnout okamžiku před příkazem DELETE. Přibližně jsme věděli fyzický čas smazání. Smazali jsme recovery_min_apply_delay a přidali recovery_target_time v recovery.conf. Tak replikace dosáhne potřebného okamžiku bez prodlení:
recovery_target_time = '2018-10-12 09:25:00+00'S časovými razítky je lepší ubrat víc, abychom se netrefili vedle. Pravda, čím víc ubráníme, tím víc dat ztrácíme. Znovu, pokud přehlédneme příkaz DELETE, všechno se znovu smaže a budeme muset začít znovu (nebo úplně vzít studenou zálohu pro PITR).
Restartovali jsme odloženou instanci Postgres a segmenty WAL se opakovaly do uvedeného času. Pokrok v této fázi lze sledovat dotazem:
SELECT
-- aktuální umístění v WAL
pg_last_xlog_replay_location(),
-- aktuální časová značka transakce (stav repliky)
pg_last_xact_replay_timestamp(),
-- aktuální fyzický čas
now(),
-- množství času, které je stále třeba aplikovat, dokud nebude dosaženo recovery_target_time
'2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;Pokud se časová značka už nemění, obnovení je dokončeno. Lze nastavit akci , která uzavře, posune nebo pozastaví instanci po opakování (výchozí je pozastavení).
Databáze se dostala do stavu před onou nešťastnou žádostí. Nyní je možné například exportovat data. Exportovali jsme odstraněná data o štítku a všechny vazby na úkoly a merge requesty a přenesli je do pracovní databáze. Pokud jsou ztráty rozsáhlé, lze replikaci jednoduše posunout a použít ji jako primární. Ale v tom případě se ztratí všechny změny od okamžiku, do kterého jsme se vrátili.
Místo časových značek je lepší používat ID transakcí. Je užitečné zapisovat tato ID, například pro operátory DDL (typu DROP TABLE), pomocí log_statements = 'ddl'. Kdybychom měli ID transakce, vzali bychom recovery_target_xid a spustili všechno až do transakce před žádostí. DELETE.
Vrátit se k práci je velmi jednoduché: odstraňte všechny změny z recovery.conf a restartujte Postgres. Brzy se v replikaci opět objeví osmihodinové zpoždění a jsme připraveni na budoucí potíže.
Výhody pro obnovu
S odloženou replikou není třeba hodiny obnovovat celou snímku z archivu. Například nám trvá pět hodin, než získáme celý základní zálohu o velikosti 2 TB. A pak ještě musíme aplikovat celé denní WAL, abychom se vrátili do požadovaného stavu (v nejhorším případě).
Odložená replika je lepší než studená záloha z dvou důvodů:
- Není nutné získávat celou základní zálohu z archivu.
- Existuje pevné osmihodinové okno segmentů WAL, které je třeba opakovat.
A také neustále kontrolujeme, zda lze provádět PITR z WAL, a rychle bychom si všimli poškození nebo jiných problémů s archívem WAL, sledováním zpoždění odložené repliky.
V tomto příkladu nám obnovování trvalo 50 minut, což znamená, že rychlost byla 110 GB dat WAL za hodinu (archív tehdy stále byl na ). Celkem jsme problém vyřešili a obnovili data za 1,5 hodiny.
Shrnutí: kde se hodí zpožděná replikace (a kde ne)
Použijte zpožděnou replikaci jako první pomoc, pokud jste náhodou ztratili data a všimli si této potíže v rámci nastaveného zpoždění.
Ale pamatujte: replikace není záloha.
Záloha a replikace mají různé cíle. Studená záloha je užitečná, pokud jste náhodou udělali DELETE nebo DROP TABLE. Děláme zálohu ze studeného úložiště a obnovujeme předchozí stav tabulky nebo celé databáze. Ale zároveň je dotaz DROP TABLE téměř okamžitě reprodukován ve všech replikách na pracovním clusteru, takže obvyklá replikace tu nezachrání. Sama o sobě replikace udržuje databázi dostupnou, když se odevzdávají jednotlivé servery, a rozděluje zátěž.
I s zpožděnou replikací nám někdy velmi pomůže studená záloha na bezpečném místě, pokud dojde k výpadku datového centra, skrytému poškození nebo jiným událostem, které okamžitě nezaznamenáte. Tady od jedné replikace není žádný užitek.
Poznámka. Na chráníme nyní ztrátu dat pouze na úrovni systému a neobnovujeme data na úrovni uživatele.
Zdroj: habr.com
