Dans cette entrée de blog, nous parlerons de la réplication logique dans PostgreSQL :ses cas d'utilisation, des informations générales sur l'état de cette technologie, et un cas d'utilisation particulier, en particulier sur la configuration d'un nœud abonné (réplique) du serveur primaire afin pour fonctionner comme serveur de base de données pour l'environnement de test, et les défis relevés.
Introduction
La réplication logique, officiellement introduite dans PostgreSQL 10, est la dernière technologie de réplication proposée par la communauté PostgreSQL. La réplication logique est une continuation de l'héritage de la réplication physique avec laquelle elle partage beaucoup d'idées et de code. La réplication logique fonctionne comme la réplication physique en utilisant le WAL pour enregistrer les changements logiques indépendamment de la version ou de l'architecture spécifique. Afin d'être en mesure de fournir une réplication logique à l'offre principale, la communauté PostgreSQL a parcouru un long chemin.
Types de réplication et historique de la réplication PostgreSQL
Les types de réplication dans les bases de données peuvent être classés comme suit :
- Réplication physique (alias binaire)
- Niveau du système d'exploitation (réplication vSphere)
- Niveau du système de fichiers (DRBD)
- Niveau de la base de données (basé sur WAL)
- Réplication logique (niveau base de données)
- Basé sur les déclencheurs (DBMirror, Slony)
- Middleware (pgpool)
- Basé sur WAL (pglogical, réplication logique)
La feuille de route qui amène à la réplication logique basée sur WAL d'aujourd'hui était :
- 2001 :DBMirror (basé sur des déclencheurs)
- 2004 :Slony1 (basé sur des déclencheurs), pgpool (middleware)
- 2005 :PITR (basé sur WAL) introduit dans PostgreSQL 8.0
- 2006 :veille à chaud dans PostgreSQL 8.2
- 2010 :Réplication physique en continu, secours à chaud dans PostgreSQL 9.0
- 2011 :Réplication en continu synchrone dans PostgreSQL 9.1
- 2012 :Réplication en streaming en cascade dans PostgreSQL 9.2
- 2013 :Travailleurs en arrière-plan dans PostgreSQL 9.3
- 2014 :API de décodage logique, slots de réplication. (Les bases de la réplication logique) dans PostgreSQL 9.4
- 2015 :2ndQuadrant présente pglogical, l'ancêtre de la réplication logique
- 2017 :Réplication logique dans le cœur de PostgreSQL 10 !
Comme nous pouvons le voir, de nombreuses technologies ont collaboré pour faire de la réplication logique une réalité :archivage WAL, secours chauds/à chaud, réplication WAL physique, travailleurs en arrière-plan, décodage logique. En supposant que le lecteur soit familiarisé avec la plupart des notions de réplication physique, nous parlerons des composants de base de la réplication logique.
Concepts de base de la réplication logique PostgreSQL
Quelques termes :
- Publication : Ensemble de modifications d'un ensemble de tables définies dans une base de données spécifique sur un serveur principal de réplication physique. Une publication peut gérer tout ou partie de :INSERT, DELETE, UPDATE, TRUNCATE.
- Nœud d'éditeur : Le serveur sur lequel réside la publication.
- Identité dupliquée : Un moyen d'identifier la ligne côté abonné pour les UPDATE et les DELETE.
- Abonnement : Une connexion à un nœud d'éditeur et à une ou plusieurs publications qu'il contient. Un abonnement utilise un emplacement de réplication dédié sur l'éditeur pour la réplication. Des emplacements de réplication supplémentaires peuvent être utilisés pour l'étape de synchronisation initiale.
- Nœud d'abonné : Le serveur sur lequel réside l'abonnement.
La réplication logique suit un modèle de publication/abonnement. Un ou plusieurs abonnés peuvent s'abonner à une ou plusieurs publications sur un nœud d'éditeur. Les abonnés peuvent republier pour permettre une réplication en cascade. La réplication logique d'une table se compose de deux étapes :
- Prendre un instantané du tableau sur l'éditeur et le copier sur l'abonné
- Appliquer toutes les modifications (depuis l'instantané) dans le même ordre
La réplication logique est transactionnelle et garantit que l'ordre des modifications appliquées à l'abonné reste le même que sur l'éditeur. La réplication logique offre beaucoup plus de liberté que la réplication physique (binaire) et peut donc être utilisée de plusieurs manières :
- Réplication spécifique à une seule base de données ou table (inutile de répliquer l'ensemble du cluster)
- Définir des déclencheurs pour l'abonné pour une tâche spécifique (telle que l'anonymisation, qui est un sujet assez brûlant après l'entrée en vigueur du RGPD)
- Le fait qu'un nœud d'abonné collecte des données à partir de nombreux nœuds d'éditeur, permettant ainsi un traitement analytique centralisé
- Réplication entre différentes versions/architectures/plates-formes (mises à niveau sans temps d'arrêt)
- Utilisation du nœud d'abonné comme serveur de base de données pour un environnement de test/développement. La raison pour laquelle nous voulons cela, c'est parce que les tests par rapport à des données réelles sont le type de test le plus réaliste.
Mises en garde et restrictions
Il y a certaines choses que nous devons garder à l'esprit lors de l'utilisation de la réplication logique, certaines d'entre elles peuvent influencer certaines décisions de conception, mais d'autres peuvent conduire à des incidents critiques.
Restrictions
- Seules les opérations DML sont prises en charge. Pas de DDL. Le schéma doit être défini au préalable
- Les séquences ne sont pas répliquées
- Les grands objets ne sont pas répliqués
- Seules les tables de base simples sont prises en charge (les vues matérialisées, les tables racine de partition et les tables étrangères ne sont pas prises en charge)
Mises en garde
Le problème fondamental auquel nous devrons tôt ou tard faire face lors de l'utilisation de la réplication logique concerne les conflits sur l'abonné. L'abonné est un serveur de lecture/écriture normal qui peut agir en tant que serveur principal dans une configuration de réplication physique, ou même en tant qu'éditeur dans une configuration de réplication logique en cascade. Tant que les écritures sur les tables abonnées sont effectuées, il peut y avoir des conflits . Un conflit survient lorsque des données répliquées violent une contrainte sur la table à laquelle elles sont appliquées. Habituellement, l'opération qui provoque cela est INSERT, DELETES ou UPDATES qui n'ont aucun effet en raison de lignes manquantes ne provoqueront pas de conflit. Lorsqu'un conflit survient, la réplication s'arrête. Le travailleur en arrière-plan logique sera redémarré dans l'intervalle spécifié (wal_retrieve_retry_interval), cependant, la réplication échouera à nouveau jusqu'à ce que la cause du conflit soit résolue. Il s'agit d'une condition critique qui doit être traitée immédiatement. Si vous ne le faites pas, le emplacement de réplication sera bloqué à sa position actuelle, le nœud éditeur commencera à accumuler des WAL et inévitablement le nœud éditeur manquera d'espace disque . Un conflit est la raison la plus courante pour laquelle la réplication peut s'arrêter, mais toute autre condition erronée aura le même effet :par ex. on a ajouté une nouvelle colonne NOT NULL sur une table abonnée mais on a oublié de définir une valeur par défaut, ou on a ajouté une colonne sur une table publiée mais on a oublié de la définir sur la table abonnée, ou on s'est trompé sur son type et les deux types ne sont pas compatible. Toutes ces erreurs arrêteront la réplication. Il existe deux manières de résoudre un conflit :
- Résoudre le problème réel
- Ignorer la transaction défaillante en appelant pg_replication_origin_advance
Solution B. comme indiqué ici peut être dangereux et délicat car il s'agit essentiellement d'un processus d'essais et d'erreurs, et si l'on choisit le LSN actuel sur l'éditeur, il peut facilement se retrouver avec un système de réplication cassé car il peut y avoir des opérations entre le LSN problématique et le LSN actuel que nous voudrions conserver. La meilleure façon est donc de résoudre le problème du côté de l'abonné. Par exemple. si une violation de CLÉ UNIQUE se produit, nous pouvons mettre à jour les données sur l'abonné ou simplement supprimer la ligne. Dans un environnement de production, tout cela doit être automatisé ou au moins semi-automatisé.
Configuration des nœuds éditeur et abonné
Pour un aperçu général de la réplication logique en pratique, veuillez lire ce blog.
Les paramètres pertinents pour la réplication logique sont :
- Côté éditeur
- wal_level>="logique"
- max_replication_slots>=#abonnements + synchronisation initiale de la table
- max_wal_senders>=max_replication_slots + other_physical_standbys
- Côté abonné
- max_replication_slots>=#abonnements
- max_logical_replication_workers>=#abonnements + synchronisation initiale de la table
- max_worker_processes>=max_logical_replication_workers + 1 + max_parallel_workers
Nous nous concentrerons sur les considérations particulières qui découlent de notre objectif particulier pour lequel nous avons besoin d'une réplication logique :créer un cluster de bases de données de test à utiliser par le service de test . La publication peut être définie soit pour toutes les tables, soit table par table. Je suggère l'approche table par table car elle nous donne un maximum de flexibilité. Les étapes générales peuvent être résumées comme suit :
- Effectuez un nouvel initdb sur le nœud de l'abonné
- Vider le schéma du cluster de l'éditeur et le copier sur le nœud de l'abonné
- Créer le schéma sur l'abonné
- Décidez des tables dont vous avez besoin et de celles dont vous n'avez pas besoin.
En ce qui concerne la puce ci-dessus, il y a deux raisons pour lesquelles vous n'avez peut-être pas besoin qu'une table soit répliquée ou configurée pour la réplication :
- C'est une table factice sans importance (et peut-être devriez-vous également la supprimer de la production)
- est une table locale à l'environnement de production, ce qui signifie qu'il est parfaitement logique que la même table dans l'environnement de test (abonné) ait ses propres données
Toutes les tables participant à la réplication logique doivent avoir une REPLICA IDENTITY. Il s'agit par défaut de la CLÉ PRIMAIRE, et si elle n'est pas disponible, une clé UNIQUE peut être définie. Prochaine étape pour trouver le statut des tables en ce qui concerne REPLICA IDENTITY.
- Trouvez les tables sans candidat évident pour REPLICA IDENTITY
select table_schema||'.'||table_name from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name NOT IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY','UNIQUE')) AND table_schema NOT IN ('information_schema','pg_catalog') ;
- Trouvez les tables sans CLÉ PRIMAIRE mais avec un INDEX UNIQUE
select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'UNIQUE' EXCEPT select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'PRIMARY KEY';
- Parcourez les listes ci-dessus et décidez quoi faire avec chaque tableau
- Créer la publication avec les tables pour lesquelles il existe un PK
select 'CREATE PUBLICATION data_for_testdb_pub FOR TABLE ONLY ' || string_agg(qry.tblname,', ONLY ') FROM (select table_schema||'.'||quote_ident(table_name) as tblname from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY')) AND table_schema NOT IN( 'information_schema','pg_catalog') ORDER BY 1) as qry; \gexec
- Créez ensuite l'abonnement sur le nœud abonné
Ce qui précède copiera également les données.create subscription data_for_testdb_pub CONNECTION 'dbname=yourdb host=yourdbhost user=repmgr' PUBLICATION data_for_testdb_pub ;
- Ajoutez les tables de votre choix qui ont un index UNIQUE
Exécutez à la fois dans les nœuds éditeur et abonné, par exemple :
Sur l'éditeur :ALTER TABLE someschema.yourtable REPLICA IDENTITY USING INDEX yourindex_ukey;
Sur l'abonné :ALTER PUBLICATION data_for_testdb_pub ADD TABLE ONLY someschema.yourtable;
ALTER SUBSCRIPTION data_for_testdb_pub REFRESH PUBLICATION WITH ( COPY_DATA );
- À ce stade (synchronisation), vous devriez toujours avoir un œil sur le journal PostgreSQL sur le nœud abonné. Vous ne voulez pas d'erreurs ou quoi que ce soit (timeout) qui interdisent la poursuite de la réplication logique. RÉSOUDRE IMMÉDIATEMENT TOUTE ERREUR , ou l'éditeur continuera d'accumuler des fichiers WAL dans pg_wal et finira par manquer d'espace. Donc, vous devez vous occuper de
- Toutes les erreurs ou tout message concernant le travailleur logique qui entraîne la sortie
- Soignez également
- wal_receiver_timeout
- wal_sender_timeout
Après avoir résolu tous les problèmes, votre nœud d'abonné devrait fonctionner correctement. La question suivante est donc de savoir comment l'utiliser comme serveur de base de données de test. Vous devrez faire face à ces problèmes/problèmes :
- Anonymisation
- Clés primaires et clés uniques basées sur des violations de séquences
- Un ensemble général de bonnes pratiques
- Surveillance
Anonymisation
En ce qui concerne l'anonymisation des données personnelles imposée par le RGPD dans l'UE, vous devez TOUJOURS écrire des déclencheurs qui vident tous les champs concernant les adresses, les comptes bancaires, l'état civil, les numéros de téléphone, les e-mails, etc. Vous devez consulter votre responsable de la sécurité dans votre entreprise à propos de ce qu'il faut garder et ce qu'il faut effacer. Les déclencheurs doivent être définis comme TOUJOURS puisque le travailleur logique exécute les instructions en tant que REPLICA.
Clés primaires avec séquences
En ce qui concerne les séquences, il y aura clairement un problème avec ces touches à moins qu'elles ne soient traitées avant le début de tout test. Prenons ce cas :
- Le vendredi après-midi, vous effectuez des tests sur la base de données des abonnés en insérant une nouvelle ligne dans une table. Celui-ci aura comme ID la prochaine valeur générée par la séquence.
- Vous rentrez chez vous pour le week-end.
- Un utilisateur de production saisit une ligne dans le même tableau de la base de données de l'éditeur.
- La ligne sera répliquée en fonction de l'IDENTITÉ DE RÉPLIQUE vers le nœud de l'abonné, mais échouera en raison d'une ERREUR de violation de PK. Le travailleur en arrière-plan logique se fermera et réessayera. Mais continuera d'échouer tant que le problème persiste.
- La réplication sera bloquée. Le slot de réplication commencera à accumuler des WAL.
- L'éditeur manque d'espace disque.
- Le week-end, vous recevez un e-mail indiquant que votre nœud principal a PANIQUE !
Ainsi, afin de résoudre le problème de séquence, vous pouvez adopter l'approche suivante :
select 'SELECT setval(''' || seqrelid::regclass||''','||CASE WHEN seqincrement <0 THEN -214748364 ELSE 214748364 END||');' from pg_sequence where seqtypid=20;
\gexec
Ce qui précède consiste à définir des séquences sur une valeur suffisamment grande pour qu'elles ne se chevauchent jamais pendant une fenêtre assez grande à l'avenir, ce qui vous permet d'avoir un serveur de test sans problème.
Un ensemble de bonnes pratiques
Vous devriez vraiment dire à vos programmeurs de rendre leurs tests non persistants. Ainsi, tout test terminé doit laisser la base de données dans le même état qu'avant le test. Avec les insertions d'ID basées sur la séquence, ce n'est pas un problème, nous avons vu plus tôt une solution. Mais avec des clés UNIQUES non séquentielles (par exemple, composées), cela pourrait poser problème. Il est donc préférable de supprimer ces données de test avant qu'une ligne de production avec la même valeur n'atteigne la table abonnée.
Ici, nous devrions également ajouter le traitement des modifications de schéma. Toutes les modifications de schéma doivent également être effectuées sur l'abonné afin de ne pas interrompre le trafic DML répliqué.
Téléchargez le livre blanc aujourd'hui PostgreSQL Management &Automation with ClusterControlDécouvrez ce que vous devez savoir pour déployer, surveiller, gérer et faire évoluer PostgreSQLTélécharger le livre blancSurveillance
Vous devriez vraiment investir dans une bonne solution de surveillance. Vous devez surveiller ...
Chez l'abonné :
- TOUS les messages du journal de l'abonné qui concernent la sortie du nœud de calcul logique. L'installation d'un outil comme tail_n_mail peut vraiment aider à cela. Une configuration connue pour fonctionner :
Une fois que nous recevons une alerte provenant de tail_n_mail, nous devrions immédiatement résoudre le problème.INCLUDE: ERROR: .*publisher.* INCLUDE: ERROR: .*exited with exit.* INCLUDE: LOG: .*exited with exit.* INCLUDE: FATAL: INCLUDE: PANIC:
- pg_stat_abonnement. Pid ne doit pas être nul. De plus, le décalage devrait être faible.
Chez l'éditeur :
- pg_stat_replication. Cela devrait avoir autant de lignes qu'elles sont censées être :une pour chaque standby de réplication de streaming connecté (nœuds d'abonnés et autres standby physiques inclus).
- pg_replication_slots pour l'emplacement de l'abonné. Cela devrait être actif.
Généralement, il faut un certain temps pour que votre serveur de base de données de test idéal fonctionne sans problème, mais une fois que vous les aurez tous résolus, vos programmeurs vous remercieront de l'avoir !