Come abbiamo utilizzato la replica ritardata per il ripristino di emergenza con PostgreSQL

Come abbiamo utilizzato la replica ritardata per il ripristino di emergenza con PostgreSQL
La replica non è un backup. O no? Ecco come abbiamo utilizzato la replica differita per ripristinare l'eliminazione accidentale dei collegamenti.

Specialisti delle infrastrutture GitLab è responsabile del lavoro GitLab.com - la più grande istanza GitLab in natura. Con 3 milioni di utenti e quasi 7 milioni di progetti, è uno dei più grandi siti SaaS open source con architettura dedicata. Senza il sistema di database PostgreSQL, l'infrastruttura GitLab.com non andrà lontano e cosa stiamo facendo per garantire la tolleranza agli errori in caso di guasti quando i dati possono andare persi. È improbabile che si verifichi un simile disastro, ma siamo ben preparati e dotati di vari meccanismi di backup e replica.

La replica non è un mezzo per eseguire il backup dei database (vedi sotto). Ma ora vedremo come recuperare velocemente i dati cancellati accidentalmente utilizzando la replica lazy: on GitLab.com utente cancellato il collegamento per il progetto gitlab-ce e connessioni perse con richieste e attività di unione.

Con una replica differita, abbiamo recuperato i dati in appena 1,5 ore. Guarda come è successo.

Recupero puntuale nel tempo con PostgreSQL

PostgreSQL ha una funzione incorporata che ripristina lo stato di un database a un momento specifico. È chiamato Recupero puntuale (PITR) e utilizza gli stessi meccanismi che mantengono aggiornata la replica: partendo da uno snapshot affidabile dell'intero cluster di database (backup di base), applichiamo una serie di modifiche di stato fino a un certo punto nel tempo.

Per utilizzare questa funzionalità per il backup a freddo, eseguiamo regolarmente un backup di base del database e lo memorizziamo in un archivio (gli archivi GitLab risiedono in Archiviazione cloud di Google). Monitoriamo inoltre i cambiamenti nello stato del database archiviando il log write-ahead (registro write-ahead, WAL). E con tutto questo a posto, possiamo eseguire un PITR per il disaster recovery: iniziando con lo snapshot scattato prima del guasto e applicando le modifiche dall'archivio WAL fino al guasto.

Cos'è la replica differita?

La replica lenta è l'applicazione delle modifiche da WAL con un ritardo. Cioè, la transazione è avvenuta in un'ora X, ma apparirà nella replica con un ritardo d nel tempo X + d.

PostgreSQL ha 2 modi per configurare una replica fisica del database: ripristino del backup e replica in streaming. Ripristino da un archivio, funziona essenzialmente come PITR, ma in modo continuo: recuperiamo costantemente le modifiche dall'archivio WAL e le applichiamo alla replica. UN replica in streaming recupera direttamente il flusso WAL dall'host del database upstream. Preferiamo il ripristino dell'archivio: è più facile da gestire e ha prestazioni normali che tengono il passo con il cluster di produzione.

Come impostare il ripristino ritardato da un archivio

Opzioni di recupero descritto nel fascicolo recovery.conf. esempio:

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'

Con questi parametri abbiamo configurato una replica differita con ripristino del backup. Qui è usato Wal-e per estrarre segmenti WAL (restore_command) dall'archivio e le modifiche verranno applicate dopo otto ore (recovery_min_apply_delay). La replica controllerà le modifiche della sequenza temporale nell'archivio, ad esempio a causa di un failover del cluster (recovery_target_timeline).

С recovery_min_apply_delay È possibile impostare la replica dello streaming con un ritardo, ma in questo caso sono presenti un paio di insidie ​​correlate agli slot di replica, al feedback hot standby e così via. L'archivio WAL ti consente di evitarli.

Parametro recovery_min_apply_delay è apparso solo in PostgreSQL 9.3. Nelle versioni precedenti, per la replica differita era necessario configurare la combinazione funzioni di gestione del ripristino (pg_xlog_replay_pause(), pg_xlog_replay_resume()) o conservare i segmenti WAL in archivio per tutta la durata del ritardo.

Come fa PostgreSQL a farlo?

È interessante vedere come PostgreSQL implementa il recupero lento. Guardiamo recoveryApplyDelay(XlogReaderState). Si chiama da ciclo di ripetizione principale per ogni voce da WAL.

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

    /* nothing to do if no delay configured */
    if (recovery_min_apply_delay <= 0)
        return false;

    /* no delay is applied on a database not yet consistent */
    if (!reachedConsistency)
        return false;

    /*
     * Is it a COMMIT record?
     *
     * We deliberately choose not to delay aborts since they have no effect on
     * MVCC. We already allow replay of records that don't have a timestamp,
     * so there is already opportunity for issues caused by early conflicts on
     * standbys.
     */
    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);

    /*
     * Exit without arming the latch if it's already past time to apply this
     * record
     */
    TimestampDifference(GetCurrentTimestamp(), recoveryDelayUntilTime,
                        &secs, &microsecs);
    if (secs <= 0 && microsecs <= 0)
        return false;

    while (true)
    {
        // Shortened:
        // Use WaitLatch until we reached recoveryDelayUntilTime
        // and then
        break;
    }
    return true;
}

La conclusione è che il ritardo si basa sul tempo fisico registrato nel timestamp di commit della transazione (xtime). Come puoi vedere, il ritardo si applica solo ai commit e non influisce sulle altre voci: tutte le modifiche vengono applicate direttamente e il commit viene ritardato, quindi vedremo le modifiche solo dopo il ritardo configurato.

Come utilizzare una replica ritardata per ripristinare i dati

Supponiamo di avere un cluster di database e una replica con un ritardo di produzione di otto ore. Vediamo come recuperare i dati utilizzando un esempio eliminazione accidentale di scorciatoie.

Quando abbiamo appreso del problema, noi il ripristino dell'archivio è stato sospeso per una replica differita:

SELECT pg_xlog_replay_pause();

Con una pausa, non c'era rischio che la replica ripetesse la richiesta DELETE. Una cosa utile se hai bisogno di tempo per capire tutto.

Il punto è che la replica differita deve arrivare al momento precedente alla richiesta DELETE. Conoscevamo approssimativamente il momento fisico della rimozione. Abbiamo cancellato recovery_min_apply_delay e aggiunse recovery_target_time в recovery.conf. Ecco come la replica raggiunge il momento giusto senza indugio:

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

Con i timestamp è meglio ridurre la franchigia per non perderla. È vero, maggiore è la diminuzione, più dati perdiamo. Ancora una volta, se perdiamo la richiesta DELETE, tutto verrà nuovamente eliminato e dovrai ricominciare da capo (o anche eseguire un backup a freddo per PITR).

Abbiamo riavviato l'istanza Postgres posticipata e i segmenti WAL sono stati ripetuti fino all'ora specificata. Puoi monitorare i progressi in questa fase chiedendo:

SELECT
  -- current location in WAL
  pg_last_xlog_replay_location(),
  -- current transaction timestamp (state of the replica)
  pg_last_xact_replay_timestamp(),
  -- current physical time
  now(),
  -- the amount of time still to be applied until recovery_target_time has been reached
  '2018-10-12 09:25:00+00'::timestamptz - pg_last_xact_replay_timestamp() as delay;

Se il timestamp non cambia più, il ripristino è completo. L'azione può essere personalizzata recovery_target_actionper chiudere, promuovere o mettere in pausa l'istanza dopo un nuovo tentativo (è sospesa per impostazione predefinita).

Il database è tornato allo stato precedente a quella sfortunata richiesta. Ora puoi, ad esempio, esportare i dati. Abbiamo esportato i dati delle etichette cancellate e tutti i collegamenti ai problemi e alle richieste di unione e li abbiamo spostati nel database di produzione. Se le perdite sono su larga scala, puoi semplicemente promuovere la replica e utilizzarla come principale. Ma poi tutti i cambiamenti successivi al punto in cui ci siamo ripresi andranno perduti.

Invece dei timestamp, è meglio utilizzare gli ID delle transazioni. È utile registrare questi ID, ad esempio, per le istruzioni DDL (come DROP TABLE), usando log_statements = 'ddl'. Se avessimo un ID transazione, prenderemmo recovery_target_xid e ho eseguito tutto fino alla transazione prima della richiesta DELETE.

Tornare al lavoro è molto semplice: rimuovi tutte le modifiche da recovery.conf e riavvia Postgres. Presto la replica avrà di nuovo un ritardo di otto ore e siamo preparati per problemi futuri.

Vantaggi del recupero

Con una replica differita invece di un backup a freddo, non devi dedicare ore a ripristinare l'intera immagine dall'archivio. Ad esempio, sono necessarie cinque ore per ottenere l'intero backup di base da 2 TB. E poi devi ancora applicare l'intero WAL giornaliero per ripristinare lo stato desiderato (nel peggiore dei casi).

Una replica differita è migliore di un backup a freddo in due modi:

  1. Non è necessario rimuovere l'intero backup di base dall'archivio.
  2. Esiste una finestra fissa di otto ore di segmenti WAL che devono essere ripetuti.

Inoltre controlliamo costantemente se è possibile creare un PITR da WAL e noteremo rapidamente corruzione o altri problemi con l'archivio WAL monitorando il ritardo della replica differita.

In questo esempio, ci sono voluti 50 minuti per il ripristino, il che significa che la velocità era di 110 GB di dati WAL all'ora (l'archivio era ancora attivo AWS S3). In totale, abbiamo risolto il problema e recuperato i dati in 1,5 ore.

Risultati: dove è utile una replica differita (e dove non lo è)

Utilizzare la replica ritardata come primo soccorso se si perdono accidentalmente dati e si nota questo problema entro il ritardo configurato.

Ma tieni presente: la replica non è un backup.

Il backup e la replica hanno scopi diversi. Un backup a freddo tornerà utile se lo hai fatto accidentalmente DELETE o DROP TABLE. Effettuiamo un backup dal cold storage e ripristiniamo lo stato precedente della tabella o dell'intero database. Ma allo stesso tempo la richiesta DROP TABLE viene riprodotto quasi istantaneamente in tutte le repliche sul cluster di lavoro, quindi la replica ordinaria non sarà d'aiuto in questo caso. La replica stessa mantiene il database disponibile quando i singoli server vengono affittati e distribuisce il carico.

Anche con una replica differita, a volte abbiamo davvero bisogno di un backup a freddo in un luogo sicuro se si verificano un guasto del data center, un danno nascosto o altri eventi non immediatamente evidenti. La sola replica non è di alcuna utilità in questo caso.

Nota. su GitLab.com Al momento proteggiamo solo dalla perdita di dati a livello di sistema e non recuperiamo i dati a livello di utente.

Fonte: habr.com

Aggiungi un commento