PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Comment mettre à niveau PostgreSQL 11 vers PostgreSQL 12 sans aucun temps d'arrêt

La méthode de mise à niveau de base de données la plus intuitive à laquelle vous pouvez penser est de générer une réplique dans une nouvelle version et d'y effectuer un basculement de l'application. En fait, cela fonctionne parfaitement dans d'autres moteurs. Avec PostgreSQL, cela était auparavant impossible de manière native. Pour effectuer des mises à niveau, vous deviez penser à d'autres moyens de mise à niveau, tels que l'utilisation de pg_upgrade, le vidage et la restauration, ou l'utilisation d'outils tiers tels que Slony ou Bucardo, chacun ayant ses propres mises en garde. Cela est dû à la façon dont PostgreSQL utilisait pour implémenter la réplication.

La réplication en continu PostgreSQL (la réplication PostgreSQL courante) est une réplication physique qui réplique les modifications au niveau octet par octet, créant une copie identique de la base de données sur un autre serveur. Cette méthode présente de nombreuses limites lorsque vous envisagez une mise à niveau, car vous ne pouvez tout simplement pas créer une réplique dans une version de serveur différente ou même dans une architecture différente.

Depuis PostgreSQL 10, il a implémenté une réplication logique intégrée qui, contrairement à la réplication physique, vous pouvez répliquer entre différentes versions majeures de PostgreSQL. Ceci, bien sûr, ouvre une nouvelle porte pour la mise à niveau des stratégies.

Dans ce blog, nous verrons comment vous pouvez mettre à niveau votre PostgreSQL 11 vers PostgreSQL 12 sans temps d'arrêt en utilisant la réplication logique.

Réplication logique PostgreSQL

La réplication logique est une méthode de réplication des objets de données et de leurs modifications, basée sur leur identité de réplication (généralement une clé primaire). Il est basé sur un mode de publication et d'abonnement, où un ou plusieurs abonnés s'abonnent à une ou plusieurs publications sur un nœud d'éditeur.

Une publication est un ensemble de modifications générées à partir d'une table ou d'un groupe de tables (également appelé ensemble de réplication). Le nœud où une publication est définie est appelé éditeur. Un abonnement est le côté aval de la réplication logique. Le nœud où un abonnement est défini est appelé l'abonné, et il définit la connexion à une autre base de données et un ensemble de publications (une ou plusieurs) auxquelles il souhaite s'abonner. Les abonnés extraient les données des publications auxquelles ils sont abonnés.

La réplication logique est construite avec une architecture similaire à la réplication physique en continu. Il est implémenté par les processus "walsender" et "apply". Le processus walsender lance le décodage logique du WAL et charge le plugin de décodage logique standard. Le plugin transforme les modifications lues depuis WAL vers le protocole de réplication logique et filtre les données en fonction de la spécification de publication. Les données sont ensuite transférées en continu à l'aide du protocole de réplication en continu vers le travailleur d'application, qui mappe les données sur des tables locales et applique les modifications individuelles au fur et à mesure de leur réception, dans un ordre transactionnel correct.

La réplication logique commence par prendre un instantané des données sur la base de données de l'éditeur et copier cela à l'abonné. Les données initiales des tables souscrites existantes sont prises en instantané et copiées dans une instance parallèle d'un type spécial de processus d'application. Ce processus créera son propre emplacement de réplication temporaire et copiera les données existantes. Une fois les données existantes copiées, l'agent passe en mode de synchronisation, ce qui garantit que la table est mise à un état synchronisé avec le processus d'application principal en diffusant en continu toutes les modifications survenues lors de la copie initiale des données à l'aide de la réplication logique standard. Une fois la synchronisation effectuée, le contrôle de la réplication de la table est rendu au processus d'application principal où la réplication se poursuit normalement. Les modifications apportées à l'éditeur sont envoyées à l'abonné au fur et à mesure qu'elles se produisent en temps réel.

Comment mettre à niveau PostgreSQL 11 vers PostgreSQL 12 à l'aide de la réplication logique

Nous allons configurer la réplication logique entre deux versions majeures différentes de PostgreSQL (11 et 12), et bien sûr, après que cela fonctionne, il ne s'agit que d'effectuer un basculement d'application dans le base de données avec la version la plus récente.

Nous allons effectuer les étapes suivantes pour faire fonctionner la réplication logique :

  • Configurer le nœud de l'éditeur
  • Configurer le nœud abonné
  • Créer l'utilisateur abonné
  • Créer une publication
  • Créer la structure de la table dans l'abonné
  • Créer l'abonnement
  • Vérifier l'état de la réplication

Alors commençons.

Côté éditeur, nous allons configurer les paramètres suivants dans le fichier postgresql.conf :

  • listen_addresses : Sur quelle(s) adresse(s) IP écouter. Nous utiliserons '*' pour tous.
  • wal_level : Détermine la quantité d'informations écrites dans le WAL. Nous allons le définir sur "logique".
  • max_replication_slots :spécifie le nombre maximal d'emplacements de réplication que le serveur peut prendre en charge. Il doit être défini sur au moins le nombre d'abonnements censés se connecter, plus une réserve pour la synchronisation des tables.
  • max_wal_senders : Spécifie le nombre maximal de connexions simultanées à partir de serveurs de secours ou de clients de sauvegarde de base en continu. Il doit être au moins égal à max_replication_slots plus le nombre de répliques physiques connectées en même temps.

Gardez à l'esprit que certains de ces paramètres nécessitaient un redémarrage du service PostgreSQL pour s'appliquer.

Le fichier pg_hba.conf doit également être ajusté pour permettre la réplication. Vous devez autoriser l'utilisateur de réplication à se connecter à la base de données.

Donc, sur cette base, configurons l'éditeur (dans ce cas, le serveur PostgreSQL 11) comme suit :

postgresql.conf :

listen_addresses = '*'

wal_level = logical

max_wal_senders = 8

max_replication_slots = 4

​pg_hba.conf :

# TYPE  DATABASE        USER            ADDRESS                 METHOD

host     all     rep1     10.10.10.131/32     md5

Vous devez changer l'utilisateur (dans cet exemple rep1), qui sera utilisé pour la réplication, et l'adresse IP 10.10.10.131/32 pour l'IP qui correspond à votre nœud PostgreSQL 12.

Côté abonné, il faut également que max_replication_slots soit défini. Dans ce cas, il doit être défini au moins sur le nombre d'abonnements qui seront ajoutés à l'abonné.

Les autres paramètres qui doivent également être définis ici sont :

  • max_logical_replication_workers :spécifie le nombre maximal de travailleurs de réplication logique. Cela inclut à la fois les agents d'application et les agents de synchronisation de table. Les agents de réplication logique sont extraits du pool défini par max_worker_processes. Il doit être défini au moins sur le nombre d'abonnements, plus une réserve pour la synchronisation des tables.
  • max_worker_processes :Définit le nombre maximal de processus d'arrière-plan que le système peut prendre en charge. Il peut être nécessaire de l'ajuster pour s'adapter aux agents de réplication, au moins max_logical_replication_workers + 1. Ce paramètre nécessite un redémarrage de PostgreSQL.

Ainsi, vous devez configurer l'abonné (dans ce cas, le serveur PostgreSQL 12) comme suit :

postgresql.conf :

listen_addresses = '*'

max_replication_slots = 4

max_logical_replication_workers = 4

max_worker_processes = 8

Comme ce PostgreSQL 12 sera bientôt le nouveau nœud principal, vous devriez envisager d'ajouter les paramètres wal_level et archive_mode dans cette étape, pour éviter un nouveau redémarrage du service plus tard.

wal_level = logical

archive_mode = on

Ces paramètres vous seront utiles si vous souhaitez ajouter une nouvelle réplique ou pour utiliser des sauvegardes PITR.

Dans l'éditeur, vous devez créer l'utilisateur avec lequel l'abonné se connectera :

world=# CREATE ROLE rep1 WITH LOGIN PASSWORD '*****' REPLICATION;

CREATE ROLE

Le rôle utilisé pour la connexion de réplication doit avoir l'attribut REPLICATION. L'accès pour le rôle doit être configuré dans pg_hba.conf et il doit avoir l'attribut LOGIN.

Afin de pouvoir copier les données initiales, le rôle utilisé pour la connexion de réplication doit avoir le privilège SELECT sur une table publiée.

world=# GRANT SELECT ON ALL TABLES IN SCHEMA public to rep1;

GRANT

Nous allons créer la publication pub1 dans le nœud de l'éditeur, pour toutes les tables :

world=# CREATE PUBLICATION pub1 FOR ALL TABLES;

CREATE PUBLICATION

L'utilisateur qui créera une publication doit avoir le privilège CREATE dans la base de données, mais pour créer une publication qui publie toutes les tables automatiquement, l'utilisateur doit être un superutilisateur.

Pour confirmer la publication créée, nous allons utiliser le catalogue pg_publication. Ce catalogue contient des informations sur toutes les publications créées dans la base de données.

world=# SELECT * FROM pg_publication;

-[ RECORD 1 ]+-----

pubname      | pub1

pubowner     | 10

puballtables | t

pubinsert    | t

pubupdate    | t

pubdelete    | t

pubtruncate  | t

Description des colonnes :

  • nom de pub :Nom de la publication.
  • propriétaire du pub :Propriétaire de la publication.
  • puballtables  :Si vrai, cette publication inclut automatiquement toutes les tables de la base de données, y compris celles qui seront créées ultérieurement.
  • pubinsert  :Si vrai, les opérations INSERT sont répliquées pour les tables de la publication.
  • mise à jour de publication  :Si vrai, les opérations UPDATE sont répliquées pour les tables de la publication.
  • pubdelete  :Si vrai, les opérations DELETE sont répliquées pour les tables de la publication.
  • publier  :Si vrai, les opérations TRUNCATE sont répliquées pour les tables de la publication.

Comme le schéma n'est pas répliqué, vous devez faire une sauvegarde dans PostgreSQL 11 et la restaurer dans votre PostgreSQL 12. La sauvegarde ne sera prise que pour le schéma, puisque les informations seront répliquées dans le transfert.

Dans PostgreSQL 11 :

$ pg_dumpall -s > schema.sql

Dans PostgreSQL 12 :

$ psql -d postgres -f schema.sql

Une fois que vous avez votre schéma dans PostgreSQL 12, vous devez créer l'abonnement, en remplaçant les valeurs de host, dbname, user et password par celles qui correspondent à votre environnement.

PostgreSQL 12 :

world=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=10.10.10.130 dbname=world user=rep1 password=*****' PUBLICATION pub1;

NOTICE:  created replication slot "sub1" on publisher

CREATE SUBSCRIPTION

Ce qui précède démarrera le processus de réplication, qui synchronise le contenu initial des tables dans la publication, puis commence à répliquer les modifications incrémentielles de ces tables.

L'utilisateur créant un abonnement doit être un superutilisateur. Le processus d'application de l'abonnement s'exécutera dans la base de données locale avec les privilèges d'un superutilisateur.

Pour vérifier l'abonnement créé, vous pouvez utiliser le catalogue pg_stat_subscription. Cette vue contiendra une ligne par abonnement pour le nœud de calcul principal (avec un PID nul si le nœud de calcul n'est pas en cours d'exécution) et des lignes supplémentaires pour les nœuds de calcul gérant la copie initiale des données des tables abonnées.

world=# SELECT * FROM pg_stat_subscription;

-[ RECORD 1 ]---------+------------------------------

subid                 | 16422

subname               | sub1

pid                   | 476

relid                 |

received_lsn          | 0/1771668

last_msg_send_time    | 2020-09-29 17:40:34.711411+00

last_msg_receipt_time | 2020-09-29 17:40:34.711533+00

latest_end_lsn        | 0/1771668

latest_end_time       | 2020-09-29 17:40:34.711411+00

Description des colonnes :

  • subid :OID de l'abonnement.
  • sous-nom :Nom de l'abonnement.
  • pid  :ID de processus du processus de travail d'abonnement.
  • relié :OID de la relation que le worker est en train de synchroniser ; null pour le travailleur d'application principal.
  • received_lsn :Dernier emplacement de journal à écriture anticipée reçu, la valeur initiale de ce champ étant 0.
  • last_msg_send_time :Heure d'envoi du dernier message reçu de l'expéditeur WAL d'origine.
  • last_msg_receipt_time :Heure de réception du dernier message reçu de l'expéditeur WAL d'origine.
  • latest_end_lsn  :dernier emplacement du journal à écriture anticipée signalé à l'expéditeur du WAL d'origine.
  • latest_end_time  :Heure du dernier emplacement du journal à écriture anticipée signalé à l'expéditeur du WAL d'origine.

Pour vérifier l'état de la réplication dans le nœud principal, vous pouvez utiliser pg_stat_replication :

world=# SELECT * FROM pg_stat_replication;

-[ RECORD 1 ]----+------------------------------

pid              | 527

usesysid         | 16428

usename          | rep1

application_name | sub1

client_addr      | 10.10.10.131

client_hostname  |

client_port      | 35570

backend_start    | 2020-09-29 17:40:04.404905+00

backend_xmin     |

state            | streaming

sent_lsn         | 0/1771668

write_lsn        | 0/1771668

flush_lsn        | 0/1771668

replay_lsn       | 0/1771668

write_lag        |

flush_lag        |

replay_lag       |

sync_priority    | 0

sync_state       | async

Description des colonnes :

  • pid :ID de processus d'un processus expéditeur WAL.
  • usesysid :OID de l'utilisateur connecté à ce processus d'envoi de WAL.
  • nom d'utilisateur :Nom de l'utilisateur connecté à ce processus d'envoi WAL.
  • nom_application :Nom de l'application qui est connectée à cet expéditeur WAL.
  • client_addr :Adresse IP du client connecté à cet expéditeur WAL. Si ce champ est nul, cela indique que le client est connecté via un socket Unix sur la machine serveur.
  • client_hostname :nom d'hôte du client connecté, tel que rapporté par une recherche DNS inversée de client_addr. Ce champ sera uniquement non nul pour les connexions IP, et uniquement lorsque log_hostname est activé.
  • port_client :numéro de port TCP que le client utilise pour communiquer avec cet expéditeur WAL, ou -1 si un socket Unix est utilisé.
  • backend_start :Heure à laquelle ce processus a été lancé.
  • backend_xmin  :L'horizon xmin de cette veille signalé par hot_standby_feedback.
  • état :État actuel de l'expéditeur WAL. Les valeurs possibles sont :démarrage, rattrapage, diffusion, sauvegarde et arrêt.
  • sent_lsn  :dernier emplacement du journal à écriture anticipée envoyé sur cette connexion.
  • write_lsn  :dernier emplacement du journal à écriture anticipée écrit sur le disque par ce serveur de secours.
  • flush_lsn  :dernier emplacement du journal à écriture anticipée vidé sur le disque par ce serveur de secours.
  • replay_lsn  :dernier emplacement du journal à écriture anticipée rejoué dans la base de données sur ce serveur de secours.
  • write_lag :temps écoulé entre le vidage local des WAL récents et la réception de la notification indiquant que ce serveur de secours l'a écrit (mais pas encore vidé ou appliqué).
  • flush_lag  :temps écoulé entre le vidage local des WAL récents et la réception de la notification indiquant que ce serveur de secours l'a écrit et vidé (mais pas encore appliqué).
  • replay_lag  :temps écoulé entre le vidage local des WAL récents et la réception de la notification indiquant que ce serveur de secours l'a écrit, vidé et appliqué.
  • sync_priority  :Priorité de ce serveur de secours pour être choisi comme serveur de secours synchrone dans une réplication synchrone basée sur la priorité.
  • sync_state :Etat synchrone de ce serveur de secours. Les valeurs possibles sont async, potential, sync, quorum.

Pour vérifier quand le transfert initial est terminé, vous pouvez consulter le journal PostgreSQL sur l'abonné :

2020-09-29 17:40:04.403 UTC [476] LOG:  logical replication apply worker for subscription "sub1" has started

2020-09-29 17:40:04.411 UTC [477] LOG:  logical replication table synchronization worker for subscription "sub1", table "city" has started

2020-09-29 17:40:04.422 UTC [478] LOG:  logical replication table synchronization worker for subscription "sub1", table "country" has started

2020-09-29 17:40:04.516 UTC [477] LOG:  logical replication table synchronization worker for subscription "sub1", table "city" has finished

2020-09-29 17:40:04.522 UTC [479] LOG:  logical replication table synchronization worker for subscription "sub1", table "countrylanguage" has started

2020-09-29 17:40:04.570 UTC [478] LOG:  logical replication table synchronization worker for subscription "sub1", table "country" has finished

2020-09-29 17:40:04.676 UTC [479] LOG:  logical replication table synchronization worker for subscription "sub1", table "countrylanguage" has finished

Ou vérifier la variable srsubstate sur le catalogue pg_subscription_rel. Ce catalogue contient l'état de chaque relation répliquée dans chaque souscription.

world=# SELECT * FROM pg_subscription_rel;

 srsubid | srrelid | srsubstate | srsublsn

---------+---------+------------+-----------

   16422 |   16386 | r          | 0/1771630

   16422 |   16392 | r          | 0/1771630

   16422 |   16399 | r          | 0/1771668

(3 rows)

Description des colonnes :

  • srsubid :Référence à l'abonnement.
  • srrelid :Référence à la relation.
  • srsubstate :Code d'état :i =initialiser, d =les données sont en cours de copie, s =synchronisé, r =prêt (réplication normale).
  • srsublsn  : LSN de fin pour les états s et r.

Vous pouvez insérer des enregistrements de test dans votre PostgreSQL 11 et valider que vous les avez dans votre PostgreSQL 12 :

PostgreSQL 11 :

world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5001,'city1','USA','District1',10000);

INSERT 0 1

world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5002,'city2','ITA','District2',20000);

INSERT 0 1

world=# INSERT INTO city (id,name,countrycode,district,population) VALUES (5003,'city3','CHN','District3',30000);

INSERT 0 1

PostgreSQL 12 :

world=# SELECT * FROM city WHERE id>5000;

  id  | name  | countrycode | district  | population

------+-------+-------------+-----------+------------

 5001 | city1 | USA         | District1 |      10000

 5002 | city2 | ITA         | District2 |      20000

 5003 | city3 | CHN         | District3 |      30000

(3 rows)

À ce stade, tout est prêt pour faire pointer votre application vers votre PostgreSQL 12.

Pour cela, tout d'abord, vous devez confirmer que vous n'avez pas de décalage de réplication.

Sur le nœud principal :

world=# SELECT  application_name,  pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) lag FROM pg_stat_replication;

-[ RECORD 1 ]----+-----

application_name | sub1

lag              | 0

Et maintenant, il vous suffit de changer votre point de terminaison de votre application ou équilibreur de charge (si vous en avez un) vers le nouveau serveur PostgreSQL 12.

Si vous avez un équilibreur de charge comme HAProxy, vous pouvez le configurer en utilisant PostgreSQL 11 comme actif et PostgreSQL 12 comme sauvegarde, de cette manière :

Donc, si vous venez d'arrêter l'ancien nœud principal dans PostgreSQL 11, le serveur de sauvegarde, dans ce cas dans PostgreSQL 12, commence à recevoir le trafic de manière transparente pour l'utilisateur/l'application.

À la fin de la migration, vous pouvez supprimer l'abonnement dans votre nouveau nœud principal dans PostgreSQL 12 :

world=# DROP SUBSCRIPTION sub1;

NOTICE:  dropped replication slot "sub1" on publisher

DROP SUBSCRIPTION

Et vérifiez qu'il est correctement supprimé :

world=# SELECT * FROM pg_subscription_rel;

(0 rows)

world=# SELECT * FROM pg_stat_subscription;

(0 rows)

Limites

Avant d'utiliser la réplication logique, veuillez garder à l'esprit les limitations suivantes :

  • Le schéma de la base de données et les commandes DDL ne sont pas répliqués. Le schéma initial peut être copié en utilisant pg_dump --schema-only.
  • Les données de séquence ne sont pas répliquées. Les données des colonnes de série ou d'identité soutenues par des séquences seront répliquées dans le cadre de la table, mais la séquence elle-même affichera toujours la valeur de départ sur l'abonné.
  • La réplication des commandes TRUNCATE est prise en charge, mais certaines précautions doivent être prises lors de la troncation de groupes de tables connectées par des clés étrangères. Lors de la réplication d'une action de troncation, l'abonné tronquera le même groupe de tables que celui qui a été tronqué sur l'éditeur, soit explicitement spécifié, soit implicitement collecté via CASCADE, moins les tables qui ne font pas partie de l'abonnement. Cela fonctionnera correctement si toutes les tables concernées font partie du même abonnement. Mais si certaines tables à tronquer sur l'abonné ont des liens de clé étrangère vers des tables qui ne font pas partie du même abonnement (ou d'aucun), alors l'application de l'action de troncature sur l'abonné échouera.
  • Les grands objets ne sont pas répliqués. Il n'y a pas de solution de contournement pour cela, autre que le stockage des données dans des tables normales.
  • La réplication n'est possible que de tables de base à tables de base. Autrement dit, les tables côté publication et côté abonnement doivent être des tables normales, et non des vues, des vues matérialisées, des tables racine de partition ou des tables étrangères. Dans le cas des partitions, vous pouvez répliquer une hiérarchie de partition un à un, mais vous ne pouvez pas actuellement répliquer vers une configuration partitionnée différemment.

Conclusion

Maintenir votre serveur PostgreSQL à jour en effectuant des mises à jour régulières était une tâche nécessaire mais difficile jusqu'à la version PostgreSQL 10. Heureusement maintenant c'est une autre histoire grâce à la réplication logique.

Dans ce blog, nous avons fait une brève introduction à la réplication logique, une fonctionnalité PostgreSQL introduite nativement dans la version 10, et nous vous avons montré comment elle peut vous aider à accomplir cette mise à niveau de PostgreSQL 11 vers PostgreSQL 12 avec une stratégie sans temps d'arrêt.