Kuidas kasutame viivitusega replikatsiooni hädaolukordade taastamiseks PostgreSQL-is

Kuidas kasutame viivitusega replikatsiooni hädaolukordade taastamiseks PostgreSQL-is
Replikatsioon — ei ole varundamine. Või siiski? Nii kasutasime me edasilükatud replikatsiooni taastamiseks, kogemata kustutades otseteed.

Infrastruktuuri spetsialistid GitLabis vastutavad töö eest GitLab.com — kõige suurema GitLabi instantsi eest maailmas. Siin on 3 miljonit kasutajat ja peaaegu 7 miljonit projekti, ja see on üks suurimaid avatud lähtekoodiga SaaS saite pühendatud arhitektuuriga. Ilma PostgreSQL andmebaasisüsteemita ei pääse GitLab.com kaugele, ja mida iganes me ei tee, et tagada tõrkeajalise jätkusuutlikkuse saamiseks, kui andmed võivad kaduda. Selline katastroof ei pruugi juhtuda, kuid oleme hästi ette valmistatud ja varustatud erinevate varundamis- ja replikatsioonimehhanismidega.

Replikatsioon ei ole andmebaasi varundamise vahend (vt allpool). Aga nüüd näeme, kui kiiresti saab kogemata kustutatud andmeid taastada edasilükatud replikatsiooni abil: eemaldasin GitLab.com kasutaja otsetee projekti jaoks gitlab-ce ja kaotasin sidemed ühendamise taotluste ja ülesannetega.

Edasilükatud replikatsiooniga taastasime andmed vaid 1,5 tunniga. Vaadake, kuidas see toimus.

Taastamine PostgreSQL-i ajas

PostgreSQL-l on sisseehitatud funktsioon, mis taastab andmebaasi oleku kindlal ajahetkel. Selle nimi on Punkt-aegne taastamine (PITR) ja see kasutab samu mehhanisme, mis säilitavad repliika ajakohasuse: alates usaldusväärsest kogu andmebaasi klastrist tehtud pildist (baasbackup), rakendame muudatusi olekusse kuni kindla ajani.

Selle funktsiooni kasutamiseks külma varundamise jaoks teeme regulaarselt andmebaasi baasbackup'i ja hoiame seda arhiivis (GitLabi arhiivid elavad Google'i pilveküljel). Samuti jälgime andmebaasi oleku muudatusi, arhiveerides ette kirjutamise ajakirja (write-ahead log, WAL). Kõikide nende andmetega saame teha PITR-i avariirestaurimiseks: alustame pildist, mis tehti enne viga, ja rakendame WAL-i arhiivist muudatused kuni tõrkeni.

Mis on viivitusega replikatsioon?

Viivitusega replikatsioon on muudatuste rakendamine WAL-ist viivitusega. See tähendab, et tehing toimus kell X, kuid replikatsioonis ilmneb see viivitusega d tunni võrra X + d.

PostgreSQL-is on 2 viisi andmebaasi füüsilise replika seadistamiseks: taastamine arhivist ja voogedastusreplikatsioon. Taastamine arhivist, toimib põhimõtteliselt nagu PITR, kuid pidevalt: me ekstraktime pidevalt muudatused arhivist WAL ja kohandame need replikale. Ja voogedastusreplikatsioon ekstraktib otse WAL voolu kõrgemalt andmebaasi hostilt. Eelistame taastamist arhivist – sellega on lihtsam hallata ja sellel on normaalne jõudlus, mis ei jää tööklastrist maha.

Kuidas seadistada viivitusega taastamine arhivist

Taastamisvalikud on kirjeldatud failis recovery.conf. Näide:

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'

Nende parameetritega oleme seadistanud viivitusega repliika taastamise arhivist. Siin kasutatakse wal-e WAL segmentide (restore_command) ekstraheerimiseks arhivist, ja muudatused rakendatakse kaheksa tunni pärast (recovery_min_apply_delay). Replika jälgib arhivi ajaskaala muutusi, näiteks klasteri tõrke juhtumite korral (recovery_target_timeline).

C recovery_min_apply_delay streamingu replikatsiooni on võimalik seadistada viivitusega, kuid siin on mõned takistused, mis on seotud replikatsiooni slotidega, kuuma varundamise tagasisidega jne. WAL arhiiv aitab neid vältida.

Parameeter recovery_min_apply_delay ilmus ainult PostgreSQL 9.3-s. Eelmistes versioonides tuli viivitatud replikatsiooni seadistamiseks kasutada kombinatsiooni taasterehvide haldusfunktsioonidest (pg_xlog_replay_pause(), pg_xlog_replay_resume()) või hoida WAL segmente arhiivis viivituse kestuse jooksul.

Kuidas PostgreSQL seda teeb?

Huvitav on vaadata, kuidas PostgreSQL rakendab viivitatud taastamist. Vaatame recoveryApplyDelay(XlogReaderState). Seda kutsutakse välja peamise kordusprotsessi käigus iga WAL-i kirje jaoks.

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

    /* ничего делать не нужно, если задержка не настроена */
    if (recovery_min_apply_delay <= 0)
        return false;

    /* задержка не применяется к базе данных, которая еще не является согласованной */
    if (!reachedConsistency)
        return false;

    /*
     * Это запись COMMIT?
     *
     * Мы сознательно выбираем не задерживать отмены, так как они не влияют на
     * MVCC. Мы уже разрешаем воспроизведение записей, у которых нет временной метки,
     * поэтому уже есть возможность для проблем, вызванных ранними конфликтами на
     * запасных.
     */
    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);

    /*
     * Выходите, не взводя задвижку, если уже прошло время для применения этой
     * записи
     */
    TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
                        &secs, &microsecs);
    if (secs <= 0 && microsecs <= 0)
        return false;

    while (true)
    {
        // Укорочено:
        // Используйте WaitLatch, пока не достигнем recoveryDelayUntilTime
        // и затем
        break;
    }
    return true;
}

Mõte on selles, et viivitus põhineb füüsilisel ajal, mis on fikseeritud tehingu kinnituse ajal (xtime). Nagu näha, rakendatakse viivitust ainult kinnitustele ja see ei puuduta teisi sisestusi — kõik muudatused rakendatakse otse, samas kui kinnitus lükatakse edasi, nii et näeme muudatusi alles pärast seadistatud viivitust.

Kuidas kasutada viivitavat koopiat andmete taastamiseks

Oletame, et meil on tootmises andmebaasi klaster ja kaheksa tunni viivitusega koopiad. Vaatame, kuidas andmeid taastada näite kaudu juhusliku otste kustutamise.

Kui me probleemist teada saime, peatame taastamise arhiivist viivitava koopia jaoks:

SELECT pg_xlog_replay_pause();

Peatamise ajal ei olnud meil riski, et koopia kordab päringut DELETE. Kasulik asi, kui on vaja aega kõike läbi mõelda.

Küsimus on selles, et viivitav koopia peab jõudma hetke enne päringut DELETE. Me teadsime ligikaudset füüsilist kustutamise aega. Kustutasime recovery_min_apply_delay ja lisasime recovery_target_time ühes recovery.conf. Nii jõuab koopia soovitud hetkeni viivitusteta:

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

Aja märkide puhul on parem liialdamist vältida, et eksida ei teeks. Kuid mida rohkem vähendate, seda rohkem andmeid kaotate. Taas, kui eksime päringust mööda DELETE, kõik jälle kustutatakse ja tuleb alustada uuesti (või üldse võtta külm varundus PITR jaoks).

Me taaskäivitame edasi lükatud Postgres'i eksemplari ja WAL segmentide korduvad kuni määratud ajani. Selle etapi edenemist saab jälgida päringuga:

SELECT
  -- praegune asukoht WAL'is
  pg_last_xlog_replay_location(),
  -- praegune tehingu ajatemperatuur (repliika seisund)
  pg_last_xact_replay_timestamp(),
  -- praegune füüsiline aeg
  now(),
  -- aeg, mis on veel rakendamiseks, kuni recovery_target_time on saavutatud
  '2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;

Kui ajatemperatuur enam ei muutu, on taastamine lõpetatud. Võib seadistada toimingu recovery_target_action, et sulgeda, edendada või peatada eksemplar pärast kordamist (vaikimisi peatatakse see).

Andmebaas on jõudnud enne seda kurikuulsat päringut. Nüüd saab näiteks andmeid eksportida. Me eksportisime kustutatud andmed sildist ja kõik seosed ülesannete ja sulandumisettepanekutega ning kandisime need töötavasse andmebaasi. Kui kaduded on ulatuslikud, võib lihtsalt edendada repliika ja kasutada seda põhina. kuid siis kaovad kõik muudatused pärast seda aega, millele me taastusime.

Aja aegade asemel on parem kasutada tehingu ID-sid. Need ID-d on kasulikud näiteks DDL-operaatorite jaoks (nagu DROP TABLE), kasutades log_statements = 'ddl'. Kui meil oleks tehingu ID, võtaksime recovery_target_xid ja viiksime läbi kõik kuni tehinguni, mis eelnes päringule DELETE.

Tagasi tööle saamine on väga lihtne: kustutage kõik muudatused ja recovery.conf ja taaskäivitage Postgres. Peagi ilmub replikas taas kaheksa tunni viivitus ja oleme valmis tulevaste hädadeks.

Taastamise eelised

Kavandatud replikaga ei pea tundide kaupa kogu arhiivist tagasi tõmbama. Näiteks kulub meil viis tundi, et hankida kogu 2 TB põhi varukoopia. Ja siis tuleb veel rakendada kogu ühepäevane WAL, et taastuda soovitud olekusse (halvimal juhul).

Kavandatud replikal on kaks eelist külmalt varukoopialt:

  1. Pole vaja terve põhi varukoopia arhiivist välja tõmmata.
  2. On fikseeritud kaheksa tunni akna WAL-s segmentoore, mida tuleb korrata.

Ja me kontrollime pidevalt, kas WAL-ist on võimalik PITR-i teha, ning märkaksime kiiresti kahjustusi või muid WAL arhiiviga seotud probleeme, jälgides kavandatud replikate mahajäämust.

Selles näites kulus meie taastamiseks 50 minutit, st kiirus oli 110 GB WAL andmeid tunnis (arhiiv oli siis veel) AWS S3). Kokku lahendasime probleemi ja taastamise aeg oli 1,5 tundi.

Kokkuvõtteks: kus võib abiks olla viivitusega replikatsioon (ja kus mitte)

Kasutage viivitusega replikatsiooni esmaabina, kui te juhuslikult kaotasite andmeid ja märkate seda probleemi seadedud viivituse jooksul.

Aga pidage meeles: replikatsioon ei ole varundamine.

Varundamise ja replikatsiooni eesmärgid on erinevad. Külman varundamine on kasulik, kui olete juhuslikult teinud DELETE või DROP TABLE. Teeme varundamise külmhoidlast ja taastame tabeli või kogu andmebaasi eelneva oleku. Kuid sel juhul päring DROP TABLE kordub peaaegu koheselt kõigis replikatsioonides tööklusteris, seega tavaline replikatsioon ei päästa siin. Replikatsioon ise hoiab andmebaasi kergesti saadaval, kui eraldi serverid langevad, ja jagab koormust.

Isegi viivitusega replikatsiooniga on meil mõnikord väga vajalik külmvõtt varundamine ohutus kohas, kui peaks toimuma andmekeskuse rike, varjatud kahjustus või muud sündmused, mida kohe ei märkate. Siin ei aita ainult replikatsioon.

Märkus. Lehelt GitLab.com meie kaitse andmete kadumise eest kehtib praegu ainult süsteemitasemel ega taasta andmeid kasutajatulemuses.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster