Como usamos a replicación diferida para a recuperación ante desastres con PostgreSQL

Como usamos a replicación diferida para a recuperación ante desastres con PostgreSQL
A replicación non é unha copia de seguridade. Ou non? Así é como usamos a replicación diferida para recuperar atallos de eliminación accidental.

Especialistas en infraestruturas GitLab é o responsable do traballo GitLab.com - a maior instancia de GitLab na natureza. Con 3 millóns de usuarios e case 7 millóns de proxectos, é un dos maiores sitios SaaS de código aberto cunha arquitectura dedicada. Sen o sistema de base de datos PostgreSQL, a infraestrutura de GitLab.com non irá lonxe, e que estamos facendo para garantir a tolerancia a fallos en caso de fallos cando se poidan perder datos. É pouco probable que ocorra un desastre así, pero estamos ben preparados e abastecidos con varios mecanismos de copia de seguridade e replicación.

A replicación non é un medio para facer copias de seguridade das bases de datos (ver a continuación). Pero agora veremos como recuperar rapidamente os datos borrados accidentalmente usando a replicación preguiceira: activado GitLab.com usuario eliminou o atallo para o proxecto gitlab-ce e conexións perdidas con solicitudes de combinación e tarefas.

Cunha réplica diferida, recuperamos os datos en só 1,5 horas. Mira como pasou.

Recuperación puntual con PostgreSQL

PostgreSQL ten unha función integrada que restaura o estado dunha base de datos nun momento específico. Chámase Recuperación puntual (PITR) e utiliza os mesmos mecanismos que manteñen a réplica actualizada: comezando cunha instantánea fiable de todo o clúster de bases de datos (copia de seguridade base), aplicamos unha serie de cambios de estado ata un momento determinado.

Para usar esta función para a copia de seguridade en frío, realizamos regularmente unha copia de seguridade básica da base de datos e almacenámola nun arquivo (os arquivos de GitLab viven en Almacenamento na nube de Google). Tamén supervisamos os cambios no estado da base de datos arquivando o rexistro de escritura anticipada (rexistro de escritura anticipada, WAL). E con todo isto en marcha, podemos facer un PITR para a recuperación ante desastres: comezando pola instantánea tomada antes do fallo, e aplicando os cambios desde o arquivo WAL ata o fallo.

Que é a replicación diferida?

A replicación preguiceira é a aplicación de cambios de WAL cun atraso. É dicir, a transacción produciuse nunha hora X, pero aparecerá na réplica cun atraso d nunha hora X + d.

PostgreSQL ten 2 formas de configurar unha réplica de base de datos física: recuperación de copias de seguridade e replicación por streaming. Restaurando desde un arquivo, funciona esencialmente como PITR, pero continuamente: recuperamos constantemente os cambios do arquivo WAL e aplícaos á réplica. A replicación en streaming recupera directamente o fluxo WAL do host da base de datos ascendente. Preferimos a recuperación de arquivos: é máis doado de xestionar e ten un rendemento normal que segue o ritmo do clúster de produción.

Como configurar a recuperación atrasada dun arquivo

Opcións de recuperación descrito no ficheiro recovery.conf. Un exemplo:

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 estes parámetros, configuramos unha réplica diferida con recuperación de copia de seguridade. Aquí úsase wal-e para extraer segmentos WAL (restore_command) do arquivo, e os cambios aplicaranse despois de oito horas (recovery_min_apply_delay). A réplica observará cambios na cronoloxía no arquivo, por exemplo debido a unha conmutación por falla do clúster (recovery_target_timeline).

С recovery_min_apply_delay Podes configurar a replicación en tempo real cun atraso, pero aquí hai un par de trampas relacionadas coas ranuras de replicación, os comentarios de espera activa, etc. O arquivo WAL permíteche evitalos.

Parámetro recovery_min_apply_delay apareceu só en PostgreSQL 9.3. Nas versións anteriores, para a replicación diferida cómpre configurar a combinación funcións de xestión de recuperación (pg_xlog_replay_pause(), pg_xlog_replay_resume()) ou manter os segmentos WAL no arquivo durante o tempo que dure o atraso.

Como fai isto PostgreSQL?

É interesante ver como PostgreSQL implementa a recuperación perezosa. Vexamos recoveryApplyDelay(XlogReaderState). Chámase de bucle de repetición principal para cada entrada de 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;
}

A conclusión é que o atraso baséase no tempo físico rexistrado na marca de tempo da transacción (xtime). Como podes ver, o atraso só se aplica ás confirmacións e non afecta a outras entradas: todos os cambios aplícanse directamente e a confirmación atrasase, polo que só veremos os cambios despois do atraso configurado.

Como usar unha réplica diferida para restaurar datos

Digamos que temos un clúster de bases de datos e unha réplica cun atraso de oito horas na produción. Vexamos como recuperar datos usando un exemplo eliminando atallos accidentalmente.

Cando nos enteramos do problema, nós a restauración do arquivo púxose en pausa para unha réplica diferida:

SELECT pg_xlog_replay_pause();

Cunha pausa, non tiñamos risco de que a réplica repetise a solicitude DELETE. Unha cousa útil se necesitas tempo para descubrir todo.

A cuestión é que a réplica diferida debe chegar ao momento anterior á solicitude DELETE. Coñecíamos aproximadamente o momento físico da eliminación. Borramos recovery_min_apply_delay e engadiu recovery_target_time в recovery.conf. Así é como a réplica chega ao momento axeitado sen demora:

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

Con marcas de tempo, é mellor reducir o exceso para non perder. É certo que canto maior sexa a diminución, máis datos perderemos. De novo, se perdemos a solicitude DELETE, todo será eliminado de novo e terás que comezar de novo (ou incluso facer unha copia de seguridade en frío para PITR).

Reiniciamos a instancia de Postgres diferida e os segmentos WAL repetíronse ata o momento especificado. Podes seguir o progreso nesta fase preguntando:

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 a marca de tempo xa non cambia, a recuperación está completa. A acción pódese personalizar recovery_target_actionpara pechar, promover ou pausar a instancia despois de reintento (está suspendida por defecto).

A base de datos volveu ao seu estado antes daquela desafortunada solicitude. Agora podes, por exemplo, exportar datos. Exportamos os datos de etiquetas eliminados e todas as ligazóns a problemas e solicitudes de combinación e movémolos á base de datos de produción. Se as perdas son a gran escala, pode simplemente promocionar a réplica e usala como a principal. Pero entón todos os cambios despois do punto ao que nos recuperamos perderanse.

En lugar de marcas de tempo, é mellor usar ID de transacción. É útil rexistrar estes ID, por exemplo, para instrucións DDL (como DROP TABLE), mediante o uso log_statements = 'ddl'. Se tivésemos un ID de transacción, tomariamos recovery_target_xid e executou todo ata a transacción antes da solicitude DELETE.

Volver ao traballo é moi sinxelo: elimina todos os cambios de recovery.conf e reinicie Postgres. A réplica terá en breve un atraso de oito horas de novo e estamos preparados para futuros problemas.

Beneficios de recuperación

Cunha réplica diferida en lugar dunha copia de seguridade en frío, non tes que gastar horas restaurando a imaxe completa do arquivo. Por exemplo, tardamos cinco horas en obter a copia de seguranza básica completa de 2 TB. E entón aínda tes que aplicar todo o WAL diario para recuperar o estado desexado (no peor dos casos).

Unha réplica diferida é mellor que unha copia de seguridade en frío de dous xeitos:

  1. Non é necesario eliminar a copia de seguridade básica completa do arquivo.
  2. Hai unha ventá fixa de oito horas de segmentos WAL que debe repetirse.

Tamén comprobamos constantemente se é posible facer un PITR desde WAL, e rapidamente notaríamos corrupción ou outros problemas co arquivo WAL supervisando o atraso da réplica diferida.

Neste exemplo, tardamos 50 minutos en restaurar, o que significa que a velocidade era de 110 GB de datos WAL por hora (o arquivo aínda estaba activo AWS S3). En total, resolvemos o problema e recuperamos os datos en 1,5 horas.

Resultados: onde unha réplica diferida é útil (e onde non o é)

Use a replicación atrasada como primeiros auxilios se perdeu datos accidentalmente e notou este problema dentro do atraso configurado.

Pero ten en conta: a replicación non é unha copia de seguridade.

A copia de seguridade e a replicación teñen diferentes propósitos. Unha copia de seguridade en frío será útil se o fixeches accidentalmente DELETE ou DROP TABLE. Facemos unha copia de seguridade desde o almacenamento en frío e restauramos o estado anterior da táboa ou de toda a base de datos. Pero ao mesmo tempo a petición DROP TABLE reprodúcese case ao instante en todas as réplicas do clúster de traballo, polo que a réplica ordinaria non axudará aquí. A propia replicación mantén dispoñible a base de datos cando os servidores individuais están inactivos e distribúe a carga.

Mesmo cunha réplica diferida, ás veces realmente necesitamos unha copia de seguridade en frío nun lugar seguro se se produce un fallo do centro de datos, danos ocultos ou outros eventos que non se notan inmediatamente. A replicación só non serve de nada aquí.

Nota. Activado GitLab.com Actualmente só protexemos contra a perda de datos a nivel de sistema e non recuperamos os datos a nivel de usuario.

Fonte: www.habr.com

Engadir un comentario