Cómo usamos la replicación diferida para la recuperación ante desastres con PostgreSQL

Cómo usamos la replicación diferida para la recuperación ante desastres con PostgreSQL
La replicación no es una copia de seguridad. ¿O no? Así es como utilizamos la replicación diferida para recuperarnos de la eliminación accidental de accesos directos.

Especialistas en infraestructura GitLab es responsable del trabajo. GitLab.com - la instancia de GitLab más grande de la naturaleza. Con 3 millones de usuarios y casi 7 millones de proyectos, es uno de los sitios SaaS de código abierto más grandes con una arquitectura dedicada. Sin el sistema de base de datos PostgreSQL, la infraestructura de GitLab.com no llegará muy lejos, y ¿qué estamos haciendo para garantizar la tolerancia a fallas en caso de que se produzcan fallas en las que se puedan perder datos? Es poco probable que ocurra tal desastre, pero estamos bien preparados y provistos de varios mecanismos de copia de seguridad y replicación.

La replicación no es un medio para realizar copias de seguridad de bases de datos (ver abajo). Pero ahora veremos cómo recuperar rápidamente datos eliminados accidentalmente mediante replicación diferida: GitLab.com usuario eliminó el acceso directo Para el proyecto gitlab-ce y conexiones perdidas con solicitudes y tareas de fusión.

Con una réplica diferida, recuperamos datos en solo 1,5 horas. Mira cómo pasó.

Recuperación puntual con PostgreSQL

PostgreSQL tiene una función incorporada que restaura el estado de una base de datos a un momento específico. Se llama Recuperación en un momento determinado (PITR) y utiliza los mismos mecanismos que mantienen la réplica actualizada: comenzando con una instantánea confiable de todo el clúster de la base de datos (copia de seguridad base), aplicamos una serie de cambios de estado hasta un determinado momento.

Para utilizar esta función para realizar copias de seguridad en frío, periódicamente realizamos una copia de seguridad básica de la base de datos y la almacenamos en un archivo (los archivos de GitLab se encuentran en Almacenamiento en la nube de Google). También monitoreamos los cambios en el estado de la base de datos archivando el registro de escritura anticipada (registro de escritura anticipada, WAL). Y con todo esto en su lugar, podemos hacer un PITR para la recuperación ante desastres: comenzando con la instantánea tomada antes del fallo y aplicando los cambios del archivo WAL hasta el fallo.

¿Qué es la replicación diferida?

La replicación diferida es la aplicación de cambios de WAL con retraso. Es decir, la transacción ocurrió en una hora. X, pero aparecerá en la réplica con un retraso. d h X + d.

PostgreSQL tiene 2 formas de configurar una réplica de base de datos física: recuperación de respaldo y replicación de transmisión. Restaurar desde un archivo, esencialmente funciona como PITR, pero de forma continua: recuperamos constantemente los cambios del archivo WAL y los aplicamos a la réplica. A replicación de transmisión recupera directamente la secuencia WAL del host de la base de datos ascendente. Preferimos la recuperación de archivos: es más fácil de administrar y tiene un rendimiento normal que se mantiene al día con el clúster de producción.

Cómo configurar la recuperación retrasada de un archivo

Opciones de recuperación descrito en el archivo recovery.conf. Un ejemplo:

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 estos parámetros configuramos una réplica diferida con recuperación de respaldo. aqui se usa wa-e para extraer segmentos WAL (restore_command) del archivo, y los cambios se aplicarán después de ocho horas (recovery_min_apply_delay). La réplica observará los cambios en la línea de tiempo en el archivo, por ejemplo, debido a una conmutación por error del clúster (recovery_target_timeline).

С recovery_min_apply_delay Puede configurar la replicación de transmisión con un retraso, pero aquí hay un par de problemas relacionados con las ranuras de replicación, la retroalimentación en espera activa, etc. El archivo WAL le permite evitarlos.

Parámetro recovery_min_apply_delay apareció sólo en PostgreSQL 9.3. En versiones anteriores, para la replicación diferida es necesario configurar la combinación funciones de gestión de recuperación (pg_xlog_replay_pause(), pg_xlog_replay_resume()) o mantener los segmentos WAL en el archivo mientras dure el retraso.

¿Cómo hace esto PostgreSQL?

Es interesante ver cómo PostgreSQL implementa la recuperación diferida. Miremos a recoveryApplyDelay(XlogReaderState). se llama desde 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;
}

La conclusión es que el retraso se basa en el tiempo físico registrado en la marca de tiempo de confirmación de la transacción (xtime). Como puede ver, el retraso solo se aplica a las confirmaciones y no afecta a otras entradas: todos los cambios se aplican directamente y la confirmación se retrasa, por lo que solo veremos los cambios después del retraso configurado.

Cómo utilizar una réplica retrasada para restaurar datos

Digamos que tenemos un clúster de base de datos y una réplica con un retraso de ocho horas en producción. Veamos cómo recuperar datos usando un ejemplo. borrar accidentalmente atajos.

Cuando nos enteramos del problema, la restauración del archivo ha sido pausada para una réplica diferida:

SELECT pg_xlog_replay_pause();

Con una pausa, no teníamos riesgo de que la réplica repitiera la solicitud. DELETE. Algo útil si necesitas tiempo para resolverlo todo.

La cuestión es que la réplica diferida debe llegar al momento anterior a la solicitud. DELETE. Sabíamos aproximadamente el momento físico de la expulsión. hemos eliminado recovery_min_apply_delay y agregado recovery_target_time в recovery.conf. Así es como la réplica llega al momento justo sin demora:

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

Con las marcas de tiempo, es mejor reducir el exceso para no perderse. Es cierto que cuanto mayor es la disminución, más datos perdemos. Nuevamente, si perdemos la solicitud DELETE, todo se eliminará nuevamente y tendrás que comenzar de nuevo (o incluso realizar una copia de seguridad en frío para PITR).

Reiniciamos la instancia de Postgres diferida y los segmentos WAL se repitieron hasta el tiempo especificado. Puede realizar un seguimiento del progreso en esta etapa 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;

Si la marca de tiempo ya no cambia, la recuperación estará completa. La acción se puede personalizar. recovery_target_actionpara cerrar, promover o pausar la instancia después de un reintento (está suspendida de forma predeterminada).

La base de datos volvió a su estado antes de esa desafortunada solicitud. Ahora puedes, por ejemplo, exportar datos. Exportamos los datos de las etiquetas eliminadas y todos los enlaces a problemas y solicitudes de fusión y los trasladamos a la base de datos de producción. Si las pérdidas son a gran escala, simplemente puede promocionar la réplica y utilizarla como principal. Pero entonces se perderán todos los cambios después del punto en el que nos hayamos recuperado.

En lugar de marcas de tiempo, es mejor utilizar ID de transacción. Es útil registrar estos ID, por ejemplo, para declaraciones DDL (como DROP TABLE), mediante el uso log_statements = 'ddl'. Si tuviéramos un ID de transacción, tomaríamos recovery_target_xid y ejecutó todo hasta la transacción antes de la solicitud DELETE.

Volver al trabajo es muy sencillo: elimina todos los cambios de recovery.conf y reinicie Postgres. La réplica pronto volverá a tener un retraso de ocho horas y estamos preparados para problemas futuros.

Beneficios de recuperación

Con una réplica diferida en lugar de una copia de seguridad en frío, no tendrá que pasar horas restaurando la imagen completa desde el archivo. Por ejemplo, nos lleva cinco horas obtener la copia de seguridad básica completa de 2 TB. Y luego aún tendrá que aplicar todo el WAL diario para recuperarse al estado deseado (en el peor de los casos).

Una réplica diferida es mejor que una copia de seguridad en frío de dos maneras:

  1. No es necesario eliminar toda la copia de seguridad básica del archivo.
  2. Hay una ventana fija de ocho horas de segmentos WAL que deben repetirse.

También verificamos constantemente si es posible crear un PITR desde WAL, y rápidamente notaríamos corrupción u otros problemas con el archivo WAL al monitorear el retraso de la réplica diferida.

En este ejemplo, nos tomó 50 minutos restaurar, lo que significa que la velocidad fue de 110 GB de datos WAL por hora (el archivo todavía estaba en AWS S3). En total, solucionamos el problema y recuperamos los datos en 1,5 horas.

Resultados: dónde es útil una réplica diferida (y dónde no)

Utilice la replicación retrasada como primeros auxilios si perdió datos accidentalmente y notó este problema dentro del retraso configurado.

Pero tenga en cuenta: la replicación no es una copia de seguridad.

La copia de seguridad y la replicación tienen diferentes propósitos. Una copia de seguridad en frío será útil si accidentalmente realizaste DELETE o DROP TABLE. Realizamos una copia de seguridad desde el almacenamiento en frío y restauramos el estado anterior de la tabla o de toda la base de datos. Pero al mismo tiempo la petición DROP TABLE se reproduce casi instantáneamente en todas las réplicas del clúster de trabajo, por lo que la replicación ordinaria no ayudará aquí. La replicación en sí mantiene la base de datos disponible cuando se alquilan servidores individuales y distribuye la carga.

Incluso con una réplica diferida, a veces realmente necesitamos una copia de seguridad en frío en un lugar seguro si ocurre una falla en el centro de datos, daños ocultos u otros eventos que no se notan de inmediato. La replicación por sí sola no sirve aquí.

Nota. En GitLab.com Actualmente solo protegemos contra la pérdida de datos a nivel del sistema y no recuperamos datos a nivel de usuario.

Fuente: habr.com

Añadir un comentario