Aller au contenu

Lag de réplica RDS : les causes et quoi faire à chaque seuil

Le lag de réplica RDS a des causes différentes selon son ampleur : des secondes, ce sont des rafales d'écriture ou le dimensionnement du réplica; des minutes, une charge coincée; NULL, la réplication est brisée. Voici le diagnostic à chaque seuil.

Le lag de réplica est le délai entre la validation d’une écriture sur le principal et sa visibilité sur un réplica de lecture, exposé comme la métrique CloudWatch ReplicaLag (en secondes) pour RDS MySQL, MariaDB et Postgres, et comme AuroraReplicaLag (en millisecondes) pour Aurora. De la sous-seconde à quelques secondes pendant des rafales d’écriture, c’est de la physique normale; des dizaines de secondes soutenues signifient que le réplica n’arrive pas à suivre le débit d’écriture du principal; un lag qui ne fait que grandir — ou un Seconds_Behind_Source à NULL sur MySQL — signifie que la réplication s’est arrêtée et que le réplica est maintenant un instantané qui pourrit lentement. Chaque seuil a son propre plan de match.

Ce qui se passe

Sur RDS MySQL et MariaDB, les réplicas appliquent le journal binaire du principal. Historiquement, le côté application était mono-thread — un principal qui valide sur 16 cœurs se déverse dans un réplica qui applique les changements avec bien moins de parallélisme, donc une rafale d’écritures sur le principal ne peut physiquement pas être rejouée au même rythme. Les versions modernes du moteur supportent l’application parallèle (replica_parallel_workers et paramètres reliés), ce qui aide mais n’abolit pas l’asymétrie. Les transactions longues sont l’autre classique : un UPDATE de 20 minutes sur le principal apparaît dans le binlog comme du travail que le réplica doit mâcher, et le lag bondit d’environ la durée de la transaction.

Sur RDS Postgres, les réplicas appliquent le WAL. Le mode de défaillance distinctif, ce sont les pauses d’application : les requêtes longues sur le réplica entrent en conflit avec le WAL entrant (un nettoyage de vacuum sur le principal veut supprimer des lignes qu’une requête du réplica lit encore), et le réplica tue votre requête ou retarde l’application du WAL, selon max_standby_streaming_delay et selon que hot_standby_feedback est activé. Le lag d’un réplica Postgres qui roule de grosses analyses est souvent auto-infligé par ces requêtes mêmes.

Aurora, c’est une toute autre plomberie — les réplicas partagent la couche de stockage, donc AuroraReplicaLag mesure la propagation du cache de pages et se tient normalement dans les basses dizaines de millisecondes; des secondes soutenues sur Aurora signalent un lecteur réellement surchargé.

Deux aggravants universels : un réplica sur une classe d’instance plus petite ou un stockage moins performant que son principal (il doit faire le même travail d’écriture avec moins de matériel, en plus de servir des lectures), et l’absence de marge binlog/WAL, où le lag devient une réplication brisée dès que le principal purge des journaux que le réplica n’a pas encore récupérés.

Quoi vérifier en premier

  • La forme de ReplicaLag dans RDS > Databases > votre-réplica > Monitoring — des pics qui se résorbent suivent les rafales d’écriture; un escalier qui ne redescend jamais, c’est un réplica arrêté ou en train de se noyer.
  • La classe d’instance et le stockage du réplica vs le principal — le réplica plus petit est l’erreur par défaut silencieuse.
  • WriteIOPS du principal superposé au lag — si le lag monte à chaque rafale d’écriture, c’est du débit, pas de la casse.
  • Les transactions longues ou tâches par lots sur le principal au moment où le lag commence; sur Postgres, les requêtes longues sur le réplica aussi.
  • Le statut de réplication depuis le moteur (ci-dessous) — les chiffres de lag ne veulent rien dire si la réplication est en fait arrêtée.

Comment vérifier

aws cloudwatch get-metric-statistics \
  --namespace AWS/RDS --metric-name ReplicaLag \
  --dimensions Name=DBInstanceIdentifier,Value=prod-db-replica1 \
  --start-time 2026-07-04T00:00:00Z --end-time 2026-07-04T12:00:00Z \
  --period 300 --statistics Maximum

Sur le réplica lui-même :

-- MySQL 8.0+ : état, lag et toute erreur d'application
SHOW REPLICA STATUS\G
-- Champs clés : Replica_IO_Running, Replica_SQL_Running,
-- Seconds_Behind_Source (NULL = brisé), Last_SQL_Error

-- Postgres : le WAL est-il reçu et appliqué, et quel est le retard
SELECT status, receive_start_lsn FROM pg_stat_wal_receiver;
SELECT now() - pg_last_xact_replay_timestamp() AS replay_delay;

Sur MySQL, Replica_SQL_Running: No avec un Last_SQL_Error rempli est la preuve du brisé-pas-lent — habituellement une écriture arrivée directement sur le réplica (quelqu’un a écrit sur un réplica inscriptible) ou une incohérence de données.

Le runbook

  1. Sous ~5 secondes, intermittent : n’y touchez pas, mais routez honnêtement. C’est la physique normale de la réplication sous écritures en rafales. Le correctif est côté application : tout chemin lecture-après-écriture (l’utilisateur enregistre, puis consulte immédiatement) doit lire depuis le principal. Les patrons d’épinglage de session et de tolérance aux lectures périmées sont couverts dans réplicas de lecture dans un SaaS multi-locataires.
  2. 10-60 secondes soutenues : trouver le déséquilibre de débit. Alignez la classe d’instance et la configuration de stockage du réplica sur celles du principal. Activez l’application parallèle sur les réplicas de la famille MySQL (replica_parallel_workers dimensionné aux cœurs, replica_preserve_commit_order activé). Découpez les grosses écritures par lots sur le principal en transactions plus petites — un UPDATE d’un million de lignes devient cent morceaux de 10 k lignes.
  3. Spécifique à Postgres : résoudre le triangle de conflit. Si le lag monte pendant que les analyses roulent sur le réplica, choisissez délibérément : hot_standby_feedback = on protège les requêtes du réplica mais cause du gonflement sur le principal; un max_standby_streaming_delay plus grand laisse le lag grandir pour protéger les requêtes; annuler les requêtes protège la fraîcheur. Vous obtenez deux des trois — choisissez selon le rôle du réplica.
  4. Des minutes et ça grimpe : triagez comme un incident. Arrêtez les écritures non essentielles ou les tâches par lots sur le principal, suspendez le trafic de lecture vers le réplica en retard (il sert des données de plus en plus périmées), et regardez si le lag plafonne. Vérifiez FreeStorageSpace sur le principal — les binlogs/WAL retenus pour un réplica mort remplissent les disques, ce qui est sa propre urgence : stockage RDS plein.
  5. NULL / arrêté / en erreur : réparer ou reconstruire. Sur MySQL, un réplica arrêté sur une erreur SQL peut parfois reprendre (CALL mysql.rds_start_replication;) après résolution de la cause, mais sauter des erreurs achète de la divergence silencieuse. Passé le point où le principal a purgé les journaux dont le réplica a besoin, il n’y a pas de réparation — supprimez et recréez le réplica. Reconstruire est plate et correct; soigner un réplica divergent n’est ni l’un ni l’autre.
  6. Lag chronique avec tout réglé : la charge a dépassé le patron. Options en ordre croissant d’effort : un autre réplica pour répartir la charge de lecture, déplacer les analyses vers un réplica dédié à fraîcheur relâchée, ou Aurora, dont les réplicas à stockage partagé éliminent le lag d’application comme catégorie — les compromis sont dans Aurora vs RDS.

Quand c’est la mauvaise approche

Ne courez pas après le lag zéro — la réplication asynchrone est asynchrone, et viser « toujours sous une seconde » sur RDS MySQL est un combat perdu; si l’application exige cette garantie, elle exige de lire depuis le principal ou une autre architecture (standbys lisibles Multi-AZ, Aurora). Ne « réglez » pas le lag en promouvant le réplica — la promotion brise la réplication de façon permanente et c’est un changement de topologie, pas une réparation. Et sur Aurora, n’appliquez pas les conseils de l’ère binlog; le mécanisme est différent et la documentation Amazon RDS traite les deux séparément pour de bonnes raisons.

Erreurs fréquentes

  • Faire rouler les réplicas une classe d’instance plus petite que le principal « parce qu’ils ne font que des lectures » — ils rejouent chaque écriture et servent des lectures.
  • Alerter sur le lag moyen : une moyenne de 3 secondes sur cinq minutes cache des pics de 30 secondes qui brisent les flux lecture-après-écriture.
  • Traiter Seconds_Behind_Source: NULL comme zéro. NULL veut dire brisé. Zéro veut dire à jour.
  • Sauter des erreurs de réplication sur MySQL (rds_skip_repl_error) à répétition — chaque saut est un petit acte de divergence de données que vous recroiserez lors d’une promotion.

Prévention

  • Alarme sur le maximum de ReplicaLag à un seuil que votre produit tolère réellement (souvent 10-30 s), et séparément sur la réplication arrêtée.
  • Gardez le matériel et les groupes de paramètres des réplicas alignés sur le principal, par politique.
  • Découpez les écritures par lots et cédulez les grosses tâches hors pointe; le réplica rejoue tout ce que vous faites au principal.
  • Réglez la rétention binlog (binlog retention hours) assez généreuse pour survivre à un réplica hors service toute une fin de semaine.
  • Testez le temps de reconstruction d’un réplica — c’est votre vrai chiffre de rétablissement quand la réplication brise.

Le lag de réplica est un budget de débit : le réplica doit rejouer chaque octet que le principal écrit, avec du matériel en réserve pour les lectures — réglez le budget correctement et les seuils ci-dessus restent surtout théoriques. Si personne dans votre équipe ne possède vraiment l’environnement AWS de bout en bout, c’est la racine de la plupart de ces problèmes — Stack Harbor opère des comptes AWS au quotidien sous Opérations AWS, et vous pouvez réserver une revue AWS.