Comment nous avons utilisé la réplication différée pour la reprise après sinistre avec PostgreSQL

Comment nous avons utilisé la réplication différée pour la reprise après sinistre avec PostgreSQL
La réplication n'est pas une sauvegarde. Ou non? Voici comment nous avons utilisé la réplication paresseuse pour la récupération en supprimant accidentellement des raccourcis.

spécialistes des infrastructures GitLab est responsable du travail GitLab.com - la plus grande instance de GitLab dans la nature. Avec 3 millions d'utilisateurs et près de 7 millions de projets, c'est l'un des plus gros sites SaaS open source avec une architecture dédiée. Sans le système de base de données PostgreSQL, l'infrastructure GitLab.com n'ira pas loin, et ce que nous ne faisons tout simplement pas pour la tolérance aux pannes en cas de panne lorsque vous pouvez perdre des données. Il est peu probable qu'une telle catastrophe se produise, mais nous nous sommes bien préparés et avons stocké divers mécanismes de sauvegarde et de réplication.

La réplication n'est pas votre outil de sauvegarde de base de données (voir ci-dessous). Mais maintenant, nous allons voir comment récupérer rapidement des données supprimées accidentellement à l'aide de la réplication différée : on GitLab.com utilisateur raccourci supprimé pour le projet gitlab-ce et les connexions perdues avec les demandes et les tâches de fusion.

Avec la réplique retardée, nous avons récupéré les données en seulement 1,5 heure. Voyez comment c'était.

Récupération ponctuelle avec PostgreSQL

PostgreSQL a une fonction intégrée qui restaure l'état d'une base de données à un moment précis. On l'appelle Récupération ponctuelle (PITR) et utilise les mêmes mécanismes qui maintiennent une réplique à jour : à partir d'un instantané fiable de l'ensemble du cluster de bases de données (sauvegarde de base), nous appliquons une série de changements d'état jusqu'à un certain point dans le temps.

Pour utiliser cette fonctionnalité pour une sauvegarde à froid, nous effectuons régulièrement une sauvegarde de la base de données de base et la stockons dans une archive (les archives GitLab résident dans Stockage en nuage Google). Nous surveillons également les changements d'état de la base de données en archivant un journal à écriture anticipée (journal d'écriture anticipée, WAL). Et avec tout cela, nous pouvons faire du PITR pour la reprise après sinistre : nous commençons par un instantané pris avant l'erreur et appliquons les modifications de l'archive WAL jusqu'au plantage.

Qu'est-ce que la réplication retardée ?

La réplication retardée est l'application des modifications de WAL avec un retard. Autrement dit, la transaction a eu lieu à l'heure X, mais il apparaîtra dans le réplica avec un délai d à une heure X + d.

Il existe 2 façons de configurer une réplique de base de données physique dans PostgreSQL : la restauration d'archive et la réplication en continu. Restauration à partir d'une archive, fonctionne essentiellement comme PITR, mais en continu : nous extrayons constamment des modifications de l'archive WAL et les appliquons à la réplique. UN réplication en continu récupère le flux WAL directement depuis l'hôte de la base de données en amont. Nous préférons la restauration à partir d'une archive - elle est plus facile à gérer et a des performances normales, qui ne sont pas à la traîne par rapport à un cluster de production.

Comment configurer la récupération de sauvegarde différée

Options de récupération décrit dans le dossier recovery.conf. Exemple:

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'

Avec ces paramètres, nous avons configuré une réplique retardée avec restauration d'archive. Utilisé ici Wal-E pour extraire les segments WAL (restore_command) à partir de l'archive, et les modifications seront appliquées après huit heures (recovery_min_apply_delay). Le réplica surveillera les changements de chronologie dans l'archive, par exemple en raison d'un basculement de cluster (recovery_target_timeline).

С recovery_min_apply_delay vous pouvez configurer la réplication en streaming avec latence, mais il existe quelques pièges associés aux emplacements de réplication, aux retours de disque de secours, etc. L'archive WAL les évite.

Paramètre recovery_min_apply_delay n'est apparu que dans PostgreSQL 9.3. Dans les versions précédentes, la réplication différée nécessite une combinaison de fonctions de gestion de récupération (pg_xlog_replay_pause(), pg_xlog_replay_resume()) ou conserver les segments WAL dans l'archive pendant la durée du délai.

Comment fait PostgreSQL ?

Il est intéressant de voir comment PostgreSQL implémente la restauration différée. Regardons recoveryApplyDelay(XlogReaderState). Il s'appelle de répétition de la boucle principale pour chaque entrée 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;
}

L'essentiel est que le délai est basé sur le temps physique enregistré dans l'horodatage de la validation de la transaction (xtime). Comme vous pouvez le voir, le délai s'applique uniquement aux validations et n'affecte pas les autres enregistrements - toutes les modifications sont appliquées directement et la validation est retardée, nous ne verrons donc les modifications qu'après le délai configuré.

Comment utiliser une réplique paresseuse pour la récupération de données

Supposons que nous ayons un cluster de bases de données en production et une réplique avec un délai de huit heures. Voyons comment récupérer des données à l'aide d'un exemple suppression accidentelle de raccourcis.

Lorsque nous avons pris conscience du problème, nous récupération de sauvegarde en pause pour la réplique retardée :

SELECT pg_xlog_replay_pause();

Avec une pause, nous n'avions aucun risque que la réplique répète la demande DELETE. Chose utile si vous avez besoin de temps pour tout comprendre.

L'essentiel est que la réplique retardée doit atteindre le moment avant la demande DELETE. Nous connaissions approximativement le moment physique du retrait. Nous avons supprimé recovery_min_apply_delay et ajouté recovery_target_time в recovery.conf. Ainsi la réplique arrive au bon moment sans délai :

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

Avec les horodatages, mieux vaut réduire le dépassement pour ne pas rater. Certes, plus la diminution est importante, plus nous perdons de données. Encore une fois, si nous sautons la demande DELETE, tout sera à nouveau supprimé et vous devrez recommencer (ou même faire une sauvegarde à froid pour PITR).

Nous avons redémarré l'instance Postgres retardée et les segments WAL ont été répétés jusqu'à l'heure spécifiée. Vous pouvez suivre la progression à ce stade en interrogeant :

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 l'horodatage ne change plus, la restauration est terminée. Vous pouvez personnaliser l'action recovery_target_actionpour fermer, promouvoir ou mettre en pause l'instance après une nouvelle tentative (elle se met en pause par défaut).

La base de données est arrivée à l'état avant cette demande malheureuse. Vous pouvez maintenant, par exemple, exporter des données. Nous avons exporté les données d'étiquettes distantes et tous les liens vers les problèmes et les demandes de fusion et les avons transférés vers la base de données de production. Si les pertes sont importantes, vous pouvez simplement promouvoir la réplique et l'utiliser comme principale. Mais alors tous les changements seront perdus après le moment auquel nous avons récupéré.

Il est préférable d'utiliser des identifiants de transaction plutôt que des horodatages. Il est utile d'enregistrer ces ID, par exemple, pour les instructions DDL (telles que DROP TABLE), en utilisant log_statements = 'ddl'. Si nous avions un ID de transaction, nous prendrions recovery_target_xid et a tout exécuté jusqu'à la transaction avant la demande DELETE.

Pour reprendre le travail, c'est très simple : supprimez toutes les modifications de recovery.conf et redémarrez postgres. Bientôt, le signal aura à nouveau un retard de huit heures et nous sommes prêts pour de futurs problèmes.

Avantages de la récupération

Avec une réplique retardée, au lieu d'une sauvegarde à froid, vous n'avez pas à passer des heures à restaurer l'intégralité de l'instantané à partir de l'archive. Par exemple, nous avons besoin de cinq heures pour obtenir la totalité de la sauvegarde de base de 2 To. Et puis vous devez encore appliquer l'intégralité du WAL quotidien pour revenir à l'état souhaité (dans le pire des cas).

Une réplique retardée est préférable à une sauvegarde à froid de deux manières :

  1. Vous n'avez pas besoin d'obtenir la totalité de la sauvegarde de base à partir de l'archive.
  2. Il existe une fenêtre fixe de huit heures de segments WAL qui doivent être répétés.

De plus, nous vérifions constamment si WAL peut être PITRed, et nous remarquerions rapidement une corruption ou d'autres problèmes avec l'archive WAL en surveillant l'arriéré de la réplique retardée.

Dans cet exemple, il nous a fallu 50 minutes pour restaurer, c'est-à-dire que la vitesse était de 110 Go de données WAL par heure (l'archive était toujours sur AWS S3). Au total, nous avons résolu le problème et restauré les données en 1,5 heure.

Résumé : où une réplique retardée est utile (et où non)

Utilisez la réplication différée comme premier secours si vous perdez accidentellement des données et remarquez ce sinistre dans le délai configuré.

Mais gardez à l'esprit que la réplication n'est pas une sauvegarde.

La sauvegarde et la réplication ont des objectifs différents. Une sauvegarde à froid sera utile si vous avez accidentellement fait DELETE ou DROP TABLE. Nous effectuons une sauvegarde à partir d'un stockage à froid et restaurons l'état précédent d'une table ou d'une base de données entière. Mais en même temps la demande DROP TABLE presque instantanément reproduit dans toutes les répliques du cluster de travail, de sorte que la réplication régulière ne sera pas enregistrée ici. La réplication elle-même maintient la base de données disponible lorsque des serveurs individuels sont loués et distribue la charge.

Même avec une réplique retardée, nous avons parfois vraiment besoin d'une sauvegarde à froid dans un endroit sûr, si soudainement il y a une panne du centre de données, des dommages cachés ou d'autres événements que vous ne remarquez pas immédiatement. Ici, à partir d'une répétition, cela n'a aucun sens.

Noter. sur GitLab.com nous protégeons actuellement uniquement contre la perte de données au niveau du système et ne restaurons pas les données au niveau de l'utilisateur.

Source: habr.com

Ajouter un commentaire