relecture et améliorations

This commit is contained in:
Gregory Colpart 2018-01-27 15:09:28 +01:00
parent aa26e22f0b
commit 661266e8c6
2 changed files with 57 additions and 52 deletions

View file

@ -5,8 +5,9 @@ categories: databases
* Documentation : <https://www.postgresql.org/docs/9.6/static/index.html>
* Rôle Ansible : <https://forge.evolix.org/projects/ansible-roles/repository/show/postgresql>
* The Internals of PostgreSQL : <http://www.interdb.jp/pg/>
[PostgreSQL](https://www.postgresql.org/) est un système de gestion de base de données relationnelle et objet qui met l'accent sur le respect du standard SQL et la sécurité des données.
[PostgreSQL](https://www.postgresql.org/) est un système de gestion de base de données relationnelle et objet. PostgreSQL met l'accent sur le respect du standard SQL et l'intégrité des données, notamment avec le mécanisme des journaux de transactions (WAL : Write Ahead Logging) écrits sur le disque avant un enregistrement réél dans les fichiers de base de données.
## Installation
@ -260,7 +261,7 @@ La configuration par défaut est faite pour s'adapter à toutes sortes de machin
* **checkpoint_completion_target** : À partir de PostgreSQL 8.3, les checkpoint sont écrits pendant que le système commence à travailler sur le prochain checkpoint. Par défaut la valeur est de 0.5 (50%), et donc PostgreSQL s'occupe de finir le checkpoint quand le suivant est fini à 50%. Il est recommandé de **fixer 90%**.
* **autovacuum** : PostgreSQL réalise de nombreuses maintenance pour nettoyer la base de données. C'est une mauvaise idée de le de désactiver complètement, mais cela peut-être utile de le désactiver temporairement, notamment en cas de forte charge.
* **autovacuum** : PostgreSQL réalise de nombreuses maintenances pour nettoyer la base de données. C'est une mauvaise idée de le de désactiver complètement, mais cela peut-être utile de le désactiver temporairement, notamment en cas de forte charge.
* **wal_buffers** : La valeur par défaut est bien trop petite (64Ko), il est recommandé de la passer de 1Mo à 16Mo.
@ -557,7 +558,7 @@ Ainsi, dès qu'un WAL est marqué comme complété (`pg_xlog/archive_status/*.re
~~~
postgres$ psql -c "SELECT pg_start_backup('evobackup')"
# rsync -a --delete --exclude /pg_xlog/* --exclude /postmaster.* ~postgres/9.4/main/ backup.example.com/backup/…/base/
# rsync -a --delete --exclude /pg_xlog/* --exclude /postmaster.* ~postgres/9.4/main/ backup.example.com:/backup/…/base/
postgres$ psql -c "SELECT pg_stop_backup()"
~~~
@ -566,7 +567,7 @@ postgres$ psql -c "SELECT pg_stop_backup()"
S'assurer que PostgreSQL est éteint, et restaurer le datadir :
~~~
# rsync -a --exclude /pg_xlog/* backup.example.com/backup/…/base/ ~postgres/9.4/main/
# rsync -a --exclude /pg_xlog/* backup.example.com:/backup/…/base/ ~postgres/9.4/main/
~~~
> *Note* : à partir de PostgreSQL 10, pg_xlog se nomme pg_wall
@ -588,7 +589,7 @@ Il est possible de rejouer les WAL jusqu'à une certaine date (`recovery_target_
La mise à jour de PostgreSQL d'une version majeure à une autre implique une mise à jour du datadir.
Dans Debian, chaque version de PostgreSQL a son propre paquet (*postgresql-9.3*, *postgresql-9.4*, etc…). La mise à jour doit donc forcement se faire de manière explicite par un `apt install` du paquet en question. Il n'est pas obligatoire de faire chaque mise à jour intermédiaire pour arriver à celle voulue (i.e. on peut passer de la 9.1 à la 9.6 sans devoir passer par la 9.2, 9.3, etc…).
Dans Debian, chaque version de PostgreSQL a son propre paquet (*postgresql-9.3*, *postgresql-9.4*, etc…). La mise à jour doit donc forcément se faire de manière explicite par un `apt install` du paquet en question. Il n'est pas obligatoire de faire chaque mise à jour intermédiaire pour arriver à celle voulue (i.e. on peut passer de la 9.1 à la 9.6 sans devoir passer par la 9.2, 9.3, etc…).
### Procédure à suivre pour la mise à jour
@ -646,9 +647,9 @@ postgres_checkpoints postgres_users
Vérification via :
~~~
/usr/lib/nagios/plugins/check_pgsql -H localhost -l nagios -p PASSWORD
$ /usr/lib/nagios/plugins/check_pgsql -H localhost -l nagios -p PASSWORD
OK - database template1 (0.011915 sec.)|time=0.011915s;2.000000;8.000000;0.000000
OK - database template1 (0.011915 sec.)|time=0.011915s;2.000000;8.000000;0.000000
~~~
@ -683,7 +684,7 @@ $ vacuumdb -a -f -v
## Benchmark
`pgbench` est un outil intégré à PostgreSQL (dans Debian, il est dans le paquet `postgresql-contrib` - /usr/lib/postgresql/8.6/bin/pgbench), il permet de réaliser des tests, et d'avoir les résultats en transactions par secondes (tps). Plus d'inos sur la [doc officielle](https://www.postgresql.org/docs/9.6/static/pgbench.html).
`pgbench` est un outil intégré à PostgreSQL (dans Debian, il est dans le paquet `postgresql-contrib`), il permet de réaliser des tests, et d'avoir les résultats en transactions par secondes (tps). Plus d'inos sur la [doc officielle](https://www.postgresql.org/docs/9.6/static/pgbench.html).
Il est recommandé de réaliser 3 fois le bench pour avoir une meilleure précision.
[Un petit script PHP](http://edoceo.com/creo/pg-bench-suite) permet d'automatiser les benchs.
@ -692,7 +693,7 @@ Il est recommandé de réaliser 3 fois le bench pour avoir une meilleure précis
$ wget http://pastebin.com/download.php?i=hD570TgL -O /tmp/script.php
$ su postgres
$ createdb -O postgres -E UNICODE pgbench
$ /usr/lib/postgresql/8.6/bin/pgbench -i pgbench -s 50
$ /usr/lib/postgresql/9.6/bin/pgbench -i pgbench -s 50
~~~
### Exemple avant optimisation
@ -753,7 +754,7 @@ pg_benchmark executed 30 tests in about 5 seconds
### PgBouncer
PgBouncer, de même que PgPool-II, permet de multiplexer plusieurs connexions à PostgreSQL en une seule : PgBouncer va recevoir les multiples connexions des clients et les envoyer à PostgreSQL à travers un pool de connexions qu'il maintient de manière persistente avec le serveur. L'intérêt principal est d'offrir un gain de performance puisque, avec PostgreSQL, une nouvelle connexion signifie un fork d'un nouveau processus, ce qui coûteux pour le système.
[PgBouncer](https://pgbouncer.github.io/), de même que PgPool-II, permet de multiplexer plusieurs connexions à PostgreSQL en une seule : PgBouncer va recevoir les multiples connexions des clients et les envoyer à PostgreSQL à travers un pool de connexions qu'il maintient de manière persistente avec le serveur. L'intérêt principal est d'offrir un gain de performance puisque, avec PostgreSQL, une nouvelle connexion signifie un fork d'un nouveau processus, ce qui coûteux pour le système.
Les autres avantages sont notamment la possibilité de gérer la répartition des requêtes vers plusieurs serveurs PostgreSQL en réplication ou le redémarrage d'un serveur PostgreSQL sans coupure (les requêtes seront alors mises en file d'attente jusqu'à ce que le serveur soit à nouveau opérationnel.
@ -821,11 +822,15 @@ psql -p 6432 -U pgbpostgres pgbouncer
pgbouncer=# show help;
~~~
Il existe un plugin Munin pour PgBouncer : <https://github.com/munin-monitoring/contrib/blob/master/plugins/postgresql/pgbouncer_>
### barman
[barman](http://www.pgbarman.org/) est un outil pour gérer les sauvegardes et les restaurations de données en se basant sur les log de transactions (WAL) de PostgreSQL.
Barman s'installe généralement sur le serveur de sauvegarde :
~~~
# apt install barman barman-cli
~~~
@ -932,10 +937,10 @@ bench=# SELECT query, calls, total_time, rows, 100.0 * shared_blks_hit / nullif(
Plusieurs solutions de réplication plus ou moins avancées existent avec PostgreSQL :
* _PITR_, _Point In Time Recovery_ : copie des logs de transaction (_WAL_) sur un serveur distant pour archivage. Ils peuvent ensuite être rejoué jusqu'à un point précis en cas de perte de donnée par exemple.
* _PITR_, _Point In Time Recovery_ : copie des logs de transaction (_WAL_) sur un serveur distant pour archivage. Ils peuvent ensuite être rejoués jusqu'à un point précis en cas de perte de donnée par exemple.
* _Warm Standby_ : les _WAL_ sont copiés sous forme d'archive sur un second serveur sur lequel tourne un PostgreSQL en mode _recovery_ constant. Chaque segment reçu est rejoué par PostgreSQL. Il est alors prêt à prendre le relais en cas de panne sur le serveur maître.
* _Hot Standby_ : le principe est le même que pour le _Warm Standby_, mais le réplicat peut être interrogé en lecture. Il y a néanmois une légère différence perpétuelle entre le master et le réplicat car le *WAL* est transféré seulement lorsque l'archive a fini d'être écrite.
* _Streaming Replication_ : les données sont transférées immédiatement par un processus dédié (_walsender_) dans une connexion réseau établie avec le réplicat. Contrairement aux autres solutions, cela nécessite une légère charge supplémentaire par réplicat sur le maître pour faire tourner le processus _walsender_. En général ce système est couplé à l'envoi des *WAL* car si le réplicat est trop en retard par rapport au master, il va lire les *WAL* jusqu'à avoir rattrapé son retard puis basculera tout seul sur la *streaming replication*
* _Hot Standby_ : le principe est le même que pour le _Warm Standby_, mais le réplica peut être interrogé en lecture. Il y a néanmois une légère différence perpétuelle entre le master et le réplica car le *WAL* est transféré seulement lorsque l'archive a fini d'être écrite.
* _Streaming Replication_ : les données sont transférées immédiatement par un processus dédié (_walsender_) dans une connexion réseau établie avec le réplica. Contrairement aux autres solutions, cela nécessite une légère charge supplémentaire par réplica sur le maître pour faire tourner le processus _walsender_. En général ce système est couplé à l'envoi des *WAL* car si le réplica est trop en retard par rapport au master, il va lire les *WAL* jusqu'à avoir rattrapé son retard puis basculera tout seul sur la *streaming replication*
Pour plus de détails sur ces solutions, voir ce post sur [dba.stackexange.com](https://dba.stackexchange.com/questions/73812/postgresql-streaming-versus-file-based-replication-in-terms-of-server-behavior). Pour d'autres types de solutions pour avoir de la haute disoponibilite, PostgreSQL a [une page sur cela dans leur documentation](https://www.postgresql.org/docs/current/static/different-replication-solutions.html).

View file

@ -4,13 +4,13 @@ La réplication en flux (_Streaming Replication_) est disponible à partir de la
## Caractéristiques
* Réplication de type asynchrone (le maître et le réplicat peuvent ne pas être synchro à un instant _t_) ou synchrone (une transaction est commitée uniquement si elle a pu être écrite sur le maître et envoyé au réplicat) ;
* Réplication de type asynchrone (le maître et le réplica peuvent ne pas être synchro à un instant _t_) ou synchrone (une transaction est commitée uniquement si elle a pu être écrite sur le maître et envoyé au réplicat) ;
* réplication basée sur les journaux binaires générés par PostgreSQL (WAL, pour _Write Ahead Log_) ;
* réplication de l'intégralité des données et structures (toutes les bases de données sont répliquées, avec leurs tables et leurs données, pas de granularité possible). Cette fonctionnalité commencera à être introduite à partir de la 9.3 ;
* le serveur jouant le rôle de réplicat ne peut être interrogé qu'en lecture ;
* le serveur jouant le rôle de réplica ne peut être interrogé qu'en lecture ;
* possibilité de mettre en cascade plusieurs réplicats.
Par rapport au mode de réplication _Hot Standby_, l'avantage avec la réplication en flux est qu'il n'est pas besoin d'attendre qu'un journal soit plein et fermé pour qu'il soit communiquer au serveur réplicat. À l'inverse cela introduit une légère charge supplémentaire (au niveau CPU) puisqu'un (ou plusieurs) processus supplémentaire doit tourner sur le maître (wal_senders).
Par rapport au mode de réplication _Hot Standby_, l'avantage avec la réplication en flux est qu'il n'est pas besoin d'attendre qu'un journal soit plein et fermé pour qu'il soit communiquer au serveur réplica. À l'inverse cela introduit une légère charge supplémentaire (au niveau CPU) puisqu'un (ou plusieurs) processus supplémentaire doit tourner sur le maître (wal_senders).
## Pré-requis
@ -25,22 +25,22 @@ Voir la documentation présente sur la page principale <https://wiki.evolix.org/
## Configuration du serveur maître
> **Note** : Cette configuration est relative au serveur maître, mais elle doit également être reprise à l'identique sur le serveur réplicat, dans le cas où les rôles doivent être échangés (suite à un failover/switchover).
> **Note** : Cette configuration est relative au serveur maître, mais elle doit également être reprise à l'identique sur le serveur réplica, dans le cas où les rôles doivent être échangés (suite à un failover/switchover).
Décommenter ou ajouter les directives suivantes dans le fichier _/etc/postgresql/9.X/main/postgresql.conf_ :
Décommenter ou ajouter les directives suivantes dans le fichier `/etc/postgresql/9.6/main/postgresql.conf` :
~~~
# Nécessaire pour que le réplicat puisse se connecter.
# Nécessaire pour que le réplica puisse se connecter.
listen_addresses = '*'
# Nombre maximum de processus walsender à lancer (mettre au moins le même
# nombre que de serveur réplicat qui se connecteront dessus + 1 (reconnexions,
# nombre que de serveur réplica qui se connecteront dessus + 1 (reconnexions,
# maintenance...).
# (1 processus = 1 connexion)
max_wal_senders = 2
# « niveau de verbosité » des journaux PostgreSQL. Le niveau maximum est
# nécessaire (hot_standby) pour que le réplicat soit accessible en lecture.
# nécessaire (hot_standby) pour que le réplica soit accessible en lecture.
wal_level = hot_standby
# Activation de l'archivage des WAL. Nécessaire pour pouvoir remettre en
@ -52,54 +52,54 @@ archive_command = 'rsync %p 192.0.2.2:/srv/pg-archives/%f'
Créer un utilisateur dédié pour la réplication :
~~~
postgres=# CREATE ROLE repl WITH LOGIN REPLICATION PASSWORD 'xxxxxxxx';
postgres=# CREATE ROLE repl WITH LOGIN REPLICATION PASSWORD 'PASSWORD';
~~~
Autoriser le serveur réplicat à se connecter au maître :
Autoriser le serveur réplica à se connecter au maître :
~~~
hostssl replication repl 192.0.2.2/32 md5
~~~
## Configuration du serveur réplicat
## Configuration du serveur réplica
> **Note** : Cette configuration est relative au serveur réplicat, mais elle doit également être reprise à l'identique sur le serveur maître, en renommant le fichier _recovery.conf_ pour qu'il ne soit pas pris en compte.
> **Note** : Cette configuration est relative au serveur réplica, mais elle doit également être reprise à l'identique sur le serveur maître, en renommant le fichier `recovery.conf` pour qu'il ne soit pas pris en compte.
Décommenter ou ajouter les directives suivantes dans le fichier _/etc/postgresql/9.X/main/postgresql.conf_ :
Décommenter ou ajouter les directives suivantes dans le fichier `/etc/postgresql/9.6/main/postgresql.conf` :
~~~
# Le serveur est en mode réplicat en lecture seule
# Le serveur est en mode réplica en lecture seule
hot_standby = on
~~~
Créer un fichier _recovery.conf_ situé dans le datadir avec les info suivantes :
Créer un fichier `recovery.conf` situé dans le datadir avec les info suivantes :
~~~
standby_mode = 'on'
primary_conninfo = 'host=192.0.2.1 user=repl password=xxxxxxxx application_name=foo'
archive_cleanup_command = '/usr/lib/postgresql/9.X/bin/pg_archivecleanup /srv/pg-archives/ %r'
primary_conninfo = 'host=192.0.2.1 user=repl password=PASSWORD application_name=foo'
archive_cleanup_command = '/usr/lib/postgresql/9.6/bin/pg_archivecleanup /srv/pg-archives/ %r'
recovery_target_timeline = 'latest'
~~~
Il est nécessaire que ce fichier appartienne à l'utilisateur _postgres_, notamment en cas de promotion en master (car postgresql va renommer le fichier en recovery.done.
Il est nécessaire que ce fichier appartienne à l'utilisateur _postgres_, notamment en cas de promotion en master (car PostgreSQL va renommer le fichier en `recovery.done`) :
~~~
# chown postgres:postgres ~postgres/9.X/main/recovery.conf
# chown postgres:postgres ~postgres/9.6/main/recovery.conf
~~~
## Synchronisation initiale des données
* Arrêter PostgreSQL sur le réplicat ;
* Arrêter PostgreSQL sur le réplica ;
* sur le maître, indiquer à PostgreSQL qu'on commence une sauvegarde. Il va notamment faire un checkpoint dans son WAL courant et retourner sa position :
~~~
postgres$ psql -c "SELECT pg_start_backup('synchro initiale')"
~~~
* lancer le rsync du datadir vers le réplicat :
* lancer le rsync du datadir vers le réplica :
~~~
# rsync -avz --delete --exclude /pg_xlog/* --exclude /postmaster.* --exclude /recovery.* ~postgres/9.X/main/ 192.0.2.2:~postgres/9.X/main/
# rsync -avz --delete --exclude /pg_xlog/* --exclude /postmaster.* --exclude /recovery.* ~postgres/9.6/main/ 192.0.2.2:~postgres/9.6/main/
~~~
* indiquer à PostgreSQL que le backup est terminé :
@ -108,15 +108,15 @@ postgres$ psql -c "SELECT pg_start_backup('synchro initiale')"
postgres$ psql -c "SELECT pg_stop_backup()"
~~~
* redémarrer PostgreSQL sur le réplicat.
* redémarrer PostgreSQL sur le réplica.
## Administration
### Monitoring
Plusieurs possibilité pour surveiller la réplication :
Plusieurs possibilités pour surveiller la réplication :
* Voir la position courante dans les logs sur le maître et le réplicat (on peut en déduire si ils sont synchro ou pas) :
* Voir la position courante dans les logs sur le maître et le réplica (on peut en déduire si ils sont synchro ou pas) :
~~~
# pgrep -lf "wal (sender|receiver) process"
@ -150,29 +150,29 @@ sync_state | async
* Pour pouvoir quantifié le retard de réplication, on peut utiliser la commande [check_postgres](http://bucardo.org/check_postgres/check_postgres.pl.html) avec l'option _hot_standby_delay_ :
~~~
$ check_postgres --action=hot_standby_delay --dbhost=localhost --dbport=5432 --dbname=template1 --dbuser=nrpe --dbpass=xxxxxx --dbhost=192.0.2.2 --dbport=5432 --warning=500000 --critical=1000000
$ check_postgres --action=hot_standby_delay --dbhost=localhost --dbport=5432 --dbname=template1 --dbuser=nrpe --dbpass=PASSWORD --dbhost=192.0.2.2 --dbport=5432 --warning=500000 --critical=1000000
POSTGRES_HOT_STANDBY_DELAY OK: DB "template1" (host:192.0.2.2) 0 | time=0.09s replay_delay=12568;500000;1000000 receive-delay=8192;500000;1000000
~~~
Où localhost est le maître et 192.0.2.2 le réplicat. Les valeurs de _replay_delay_ et _receive-delay_ sont *à priori* exprimées en octets de WAL à rejouer.
Où localhost est le maître et 192.0.2.2 le réplica. Les valeurs de _replay_delay_ et _receive-delay_ sont *à priori* exprimées en octets de WAL à rejouer.
### Passer un serveur réplicat en maître
### Passer un serveur réplica en maître
Si le maître est toujours joignable, éteindre PostgreSQL en forçant la déconnexion des clients :
~~~
# pg_ctlcluster 9.X main stop -- -m fast
# pg_ctlcluster 9.6 main stop -- -m fast
~~~
Sur le réplicat, faire en sorte que PostgreSQL accepte les connexions en écriture :
Sur le réplica, faire en sorte que PostgreSQL accepte les connexions en écriture :
~~~
# pg_ctlcluster 9.X main promote
# pg_ctlcluster 9.6 main promote
~~~
Le réplicat va d'abord rattraper son éventuel retard dans le traitement des logs de réplication, puis une fois prêt se mettra à accepter les requêtes en écritures.
Le réplica va d'abord rattraper son éventuel retard dans le traitement des logs de réplication, puis une fois prêt se mettra à accepter les requêtes en écritures.
Le fichier _recovery.conf_ est renommé en _recovery.done_ pour qu'il ne soit pas lu en cas de redémarrage de PostgreSQL.
Le fichier `recovery.conf` est renommé en `recovery.done` pour qu'il ne soit pas lu en cas de redémarrage de PostgreSQL.
Messages de PostgreSQL lors du passage en maître :
@ -186,17 +186,17 @@ Messages de PostgreSQL lors du passage en maître :
2013-04-23 05:54:15 EDT LOG: autovacuum launcher started
~~~
*ATTENTION : à partir du moment où le réplicat cesse de lire les journaux du maître, toutes les écritures qui continuent de se faire sur le maître seront perdues. Il faut donc être certain que le maître soit réellement inaccessible avant de faire la bascule.*
> *Note* : Attention, à partir du moment où le réplica cesse de lire les journaux du maître, toutes les écritures qui continuent de se faire sur le maître seront perdues. Il faut donc être certain que le maître soit réellement inaccessible avant de faire la bascule.
### Rétablissement de la réplication après un failover
État courant : le serveur réplicat accepte les écritures suite à la procédure de failover, et le serveur maître contient des données obsolètes car pas mises à jour.
État courant : le serveur réplica accepte les écritures suite à la procédure de failover, et le serveur maître contient des données obsolètes car pas mises à jour.
Il faut alors mettre en place le _recovery.conf_ sur l'ancien master et démarrer PostgreSQL. Il va alors rejouer les WAL pour rattraper le retard accumulé, puis se mettre en mettre en mode _streaming replication_.
Il faut alors mettre en place le `recovery.conf` sur l'ancien master et démarrer PostgreSQL. Il va alors rejouer les WAL pour rattraper le retard accumulé, puis se mettre en mettre en mode _streaming replication_.
### Arrêter la réplication
* Arrêter/reprendre le "replay" des WAL sur le réplicat :
* Arrêter/reprendre le "replay" des WAL sur le réplica :
~~~
postgres=# SELECT pg_xlog_replay_pause();
@ -212,6 +212,6 @@ postgres=# SELECT pg_xlog_replay_resume();
## Diverses notes/spécificités de la réplication
* Si une requête sur le serveur réplicat bloque la réplication (par exemple un `SELECT` qui bloque un `DROP TABLE`) pendant un temps trop long, la requête sur le réplicat sera tuée (ici le SELECT). Ce temps est défini par la directive `max_standby_streaming_delay` sur la configuration du réplicat.
* Si une requête sur le serveur réplica bloque la réplication (par exemple un `SELECT` qui bloque un `DROP TABLE`) pendant un temps trop long, la requête sur le réplica sera tuée (ici le SELECT). Ce temps est défini par la directive `max_standby_streaming_delay` sur la configuration du réplica.
Ce comportement peut-être désactivé grâce à la directive `hot_standby_feedback`, qui fait en sorte que le réplicat communique l'état des requêtes au maître, mais cela à un impact sur le maître.
Ce comportement peut-être désactivé grâce à la directive `hot_standby_feedback`, qui fait en sorte que le réplica communique l'état des requêtes au maître, mais cela à un impact sur le maître.