
Replicația nu este backup. Sau poate? Iată cum am folosit replicația întârziată pentru a restaura, după ce am șters din greșeală scurtăturile.
pe GitLab sunt responsabili pentru funcționarea — cea mai mare instanță GitLab din lume. Aici sunt 3 milioane de utilizatori și aproape 7 milioane de proiecte, și este unul dintre cele mai mari site-uri open-source SaaS cu o arhitectură dedicată. Fără sistemul de baze de date PostgreSQL, infrastructura GitLab.com nu s-ar descurca, iar noi facem tot ce putem pentru a asigura continuitatea în cazul oricăror defecțiuni, când ar putea fi pierdute date. Deși o astfel de calamitate este puțin probabil să se întâmple, suntem bine pregătiți și am luat măsuri cu diverse mecanisme de backup și replicare.
Replicația nu este un mijloc de backup pentru baze de date (). Dar acum vom vedea cât de rapid putem restaura datele șterse din greșeală folosind replicația întârziată: la utilizator pentru proiectul și a pierdut legăturile cu cererile de fuziune și sarcinile.
Cu replicația întârziată, am restaurat datele în doar 1,5 ore. Uite cum a fost.
Restaurare la un moment dat cu PostgreSQL
PostgreSQL are o funcție încorporată care restaurează starea bazei de date la un anumit moment. Se numește (PITR) și utilizează aceleași mecanisme care susțin actualitatea replicii: începem cu o captură de siguranță a întregului cluster de baze de date (backup de bază), aplicăm o serie de modificări de stare până la un moment dat.
Pentru a utiliza această funcție pentru backup rece, facem regulat backup de bază al bazei de date și îl stocăm în arhivă (arhivele GitLab trăiesc în ). De asemenea, urmărim modificările stării bazei de date, arhivând jurnalul de scriere anticipată (, WAL). Și cu toate acestea putem efectua PITR pentru recuperare în caz de dezastru: începem cu captura efectuată înainte de eroare și aplicăm modificările din arhiva WAL până la defecțiune.
Ce este replicația întârziată?
Replicația întârziată este aplicarea modificărilor din WAL cu o întârziere. Adică tranzacția a avut loc la ora X, dar în replică va apărea cu o întârziere d la ora X + d.
În PostgreSQL există 2 moduri de a configura replicația fizică a bazei de date: restaurare din arhivă și replicație în flux. , în esență, funcționează ca PITR, dar continuu: extragem constant modificările din arhiva WAL și le aplicăm pe replică. A extrage direct fluxul WAL din gazda de bază de date superioară. Preferăm recuperarea din arhivă — este mai ușor de gestionat și are o performanță normală, care nu este inferioară celei a clusterului de lucru.
Cum să configurezi recuperarea întârziată din arhivă
sunt descrise în fișierul recovery.conf. Exemplu:
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"'\nrecovery_min_apply_delay = '8h'\nrecovery_target_timeline = 'latest'Cu aceste setări, am configurat o replică întârziată cu recuperare din arhivă. Aici se folosește pentru extragerea segmentelor WAL (restore_command) din arhivă, iar modificările vor fi aplicate după opt ore (recovery_min_apply_delay). Replica va monitoriza modificările din cronologia arhivei, de exemplu, din cauza unui eșec în cluster (recovery_target_timeline).
Cu recovery_min_apply_delay se poate configura replicarea în flux cu întârziere, dar există câteva capcane legate de sloturile de replicare, feedback-ul hot standby și altele. Arhiva WAL permite evitarea acestora.
Parametru recovery_min_apply_delay a fost introdusă abia în PostgreSQL 9.3. În versiunile anterioare, pentru replicarea întârziată era necesară configurarea unei combinații de (pg_xlog_replay_pause(), pg_xlog_replay_resume()) sau păstrarea segmentelor WAL în arhivă pentru timpul întârzierii.
Cum o face PostgreSQL?
Este interesant să vedem cum PostgreSQL implementează recuperarea întârziată. Să ne uităm la . Este apelat din pentru fiecare înregistrare din WAL.
static bool
recoveryApplyDelay(XLogReaderState *record)
{
uint8 xact_info;
TimestampTz xtime;
long secs;
int microsecs;
/* nu avem nimic de făcut dacă nu este configurată nicio întârziere */
if (recovery_min_apply_delay <= 0)
return false;
/* nicio întârziere nu se aplică pe o bază de date care nu este încă consistentă */
if (!reachedConsistency)
return false;
/*
* Este un înregistrare COMMIT?
*
* Alegem în mod intenționat să nu întârziem abaterile deoarece nu au efect pe
* MVCC. Permitem deja redarea înregistrărilor care nu au un timestamp,
* astfel că există deja oportunitatea pentru probleme cauzate de conflictele timpurii pe
* standby-uri.
* /
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);
/*
* Ieşire fără a activa latch-ul dacă a trecut deja timpul pentru a aplica acest
* înregistrare
* /
TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
&secs, µsecs);
if (secs <= 0 && microsecs <= 0)
return false;
while (true)
{
// Scurtat:
// Folosim WaitLatch până ajungem la recoveryDelayUntilTime
// și apoi
break;
}
return true;
}Ideea este că întârzierea se bazează pe timpul fizic, înregistrat în timestamp-ul comitetului tranzacției (xtime). După cum se vede, întârzierea se aplică numai la comitete și nu afectează alte înregistrări - toate modificările se aplică direct, iar comitetul este întârziat, astfel încât vom vedea modificările doar după întârzierea configurată.
Cum să folosești o replică întârziată pentru recuperarea datelor
Să presupunem că avem în producție un cluster de baze de date și o replică cu o întârziere de opt ore. Să vedem cum să recuperăm datele folosind exemplul .
Când am aflat despre problemă, am pentru replica întârziată:
SELECT pg_xlog_replay_pause();Cu pauza nu am avut riscul ca replica să repete solicitarea DELETE. O treabă utilă, dacă ai nevoie de timp pentru a înțelege totul.
Ideea este că replica întârziată trebuie să ajungă la momentul dinaintea solicitării DELETE. Știam aproximativ timpul fizic al ștergerii. Am șters recovery_min_apply_delay și am adăugat recovery_target_time în recovery.conf. Astfel, replica ajunge la momentul dorit fără întârzieri:
recovery_target_time = '2018-10-12 09:25:00+00'Cu timpii mai bine să reducă în exces, pentru a nu rata. Totuși, cu cât reduc mai mult, cu atât mai multe date se pierd. Din nou, dacă trecem peste solicitare DELETE, totul se va șterge din nou și va trebui să începem de la zero (sau chiar să luăm un backup rece pentru PITR).
Am repornit instanța amânată de Postgres, iar segmentele WAL au fost repetate până la timpul specificat. Progresul în această etapă poate fi urmărit prin cererea:
SELECT
-- locația curentă în WAL
pg_last_xlog_replay_location(),
-- timestamp-ul curent al tranzacției (starea replicii)
pg_last_xact_replay_timestamp(),
-- timpul fizic curent
now(),
-- cantitatea de timp care trebuie aplicată până ce recovery_target_time a fost atins
'2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;Dacă timestamp-ul nu se mai schimbă, recuperarea s-a încheiat. Se poate configura acțiunea , pentru a închide, avansa sau suspenda instanța după repetare (implicit este suspendată).
baza de date a revenit la starea anterioară acelui reflux nefericit. Acum se poate, de exemplu, exporta datele. Am exportat datele șterse despre etichete și toate relațiile cu sarcinile și cererile de fuziune și le-am transferat în baza de date de lucru. Dacă pierderile sunt semnificative, se poate pur și simplu avansa replica și folosi ca pe cea principală. Dar atunci se vor pierde toate modificările după momentul la care ne-am recuperat.
În loc de timestamp-uri, este mai bine să folosești ID-urile tranzacțiilor. E util să notezi aceste ID-uri, de exemplu, pentru operatorii DDL (de tipul DROP TABLE), cu ajutorul log_statements = 'ddl'. Dacă am avea ID-ul tranzacției, l-am lua pe recovery_target_xid și am rula toate până la tranzacția anterioară cererii. DELETE.
A reveni la activitate este foarte simplu: elimină toate modificările din recovery.conf și repornește Postgres. În curând, în replică va apărea din nou întârzierea de opt ore, și suntem pregătiți pentru viitoarele neplăceri.
Avantajele pentru recuperare
Cu o replică amânată în loc de un backup rece, nu trebuie să recuperăm întreaga imagine din arhivă timp de ore. De exemplu, avem nevoie de cinci ore pentru a obține întreaga bază de backup de 2 TB. Apoi, mai trebuie aplicat tot WAL-ul zilnic pentru a ne recupera la starea dorită (în cel mai rău caz).
Replica amânată este superioară backup-ului rece din două puncte de vedere:
- Nu trebuie să aducem întreaga bază de backup din arhivă.
- Există o fereastră fixă de opt ore de segmente WAL care trebuie repetate.
De asemenea, verificăm constant dacă se poate realiza PITR din WAL și am observa rapid deteriorările sau alte probleme cu arhiva WAL, urmărind întârzierea replicii amânate.
În acest exemplu, ne-a luat 50 de minute să ne recuperăm, adică viteza a fost de 110 GB de date WAL pe oră (arhiva era încă pe ). Am rezolvat problema și am restaurat datele în 1,5 ore.
Concluzii: unde este utilă replicarea întârziată (și unde nu)
Folosiți replicarea întârziată ca un instrument de prim ajutor dacă ați pierdut accidental datele și ați observat acest lucru în cadrul întârzierii configurate.
Dar rețineți: replicarea nu este un backup.
Backup-ul și replicarea au scopuri diferite. Un backup rece este util dacă ați realizat accidental DELETE sau DROP TABLE. Facem un backup dintr-un depozit rece și restaurăm starea anterioară a tabelului sau a întregii baze de date. Dar, în același timp, solicitarea DROP TABLE se reproduce aproape instantaneu în toate replicile din clusterul de lucru, așa că replicarea obișnuită nu va ajuta. Replicarea, în sine, menține baza de date disponibilă atunci când serverele sunt oprite și distribuie sarcina.
Chiar și cu o replică întârziată, uneori avem nevoie urgentă de un backup rece într-un loc sigur, în cazul în care survine o defecțiune a centrului de date, o deteriorare ascunsă sau alte evenimente care nu sunt imediat observabile. Aici, replicarea nu este de ajutor.
Notă. Pe noi protejăm în prezent împotriva pierderii datelor doar la nivel de sistem și nu restaurăm datele la nivel de utilizator.
Sursa: habr.com
