18
0
Fork 0
wiki/HowtoPostgreSQL.md

50 KiB

000000010000000300000013 (1 row)

postgres=# SELECT pg_current_xlog_location(); pg_current_xlog_location

3/138BE620 (1 row)


Ce mécanisme de WAL est à la base de PostgreSQL, et il sert notamment pour la sauvegarde ou la réplication.

Pour la sauvegarde on peut ainsi envoyer automatiquement un fichier de WAL dès qu'il est fermé avec l'option `archive_command = 'cp %p /tmp/pg_xlog_archives/%f'`.

### VACUUM et ANALYZE

`VACUUM` permet de nettoyer les données obsolètes (lignes d'une table qui ont été modifiées par un `UPDATE` par exemple) que PostgreSQL n'efface pas volontairement du disque puisque d'autres requêtes plus anciennes peuvent encore y accéder.

`ANALYZE` permet à PostgreSQL de collecter et mettre à jour ses statistiques sur la répartition des données en base afin que le planificateur de requêtes ait des données les plus précises possible.

ANALYZE sur toutes les tables d'une base de données :

=# ANALYZE;


Ces 2 opérations peuvent être exécutées en un seul coup :

=# VACUUM ANALYZE;



Par défaut, PostgreSQL lance au démarrage le processus `autovacuum launcher process` qui se charge d'exécuter des `VACUUM` et `ANALYZE` à intervalles réguliers. Les `VACUUM` et `ANALYZE` sont exécutés seulement si la quantité de données à nettoyer/réanalyser (en fait le nombre de `DELETE` et `UPDATE` que la table a reçu depuis son dernier passage) dépasse un certain seuil configurable.

`VACUUM` n'efface pas réellement les données sur le disque, il marque leurs emplacements comme libre de sorte que PostgreSQL puisse écrire dessus lorsqu'il aura à nouveau besoin d'écrire des données. Pour le forcer à libérer l'espace disque au niveau du système de fichiers, il faut exécuter un `VACUUM FULL`. Cette opération n'est pas faite par le démon `autovacuum` car elle requière de locker les tables en lecture et écriture (aucune requête n'est possible durant un `VACUUM FULL`). Le cas où l'on aurait besoin de lancer une telle opération serait après une grosse suppression de données (`DELETE`, `TRUNCATE`) et où on aurait besoin de récupérer de l'espace disque au niveau du système de fichiers pour autre chose. Dans les autres cas, il n'y a généralement pas d'utilité à lancer un `VACUUM FULL`.

Faire un VACUUM FULL sur toutes les bases de données :

$ vacuumdb -a -f -v



## Benchmark

`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.

apt install php7.0-cli postgresql-contrib

$ wget http://pastebin.com/download.php?i=hD570TgL -O /tmp/script.php $ su postgres $ createdb -O postgres -E UNICODE pgbench $ /usr/lib/postgresql/9.6/bin/pgbench -i pgbench -s 50


### Exemple avant optimisation

$ php -f script.php Shared Memory Max is 3,072.00Mb PostgreSQL is using 10.57Mb Testing: 5 clients with 5 transactions (5 samples) TPS Min 543.218461; Max: 666.471168; Avg: 590.9413884; Dev: 48.7702658268 CTPS Min: 702.760443; Max: 926.406285; Avg: 788.2504666; Dev: 89.6348872298 Testing: 5 clients with 10 transactions (5 samples) TPS Min 132.604187; Max: 808.211428; Avg: 644.4933226; Dev: 288.384840931 CTPS Min: 136.534056; Max: 973.652951; Avg: 765.8912782; Dev: 355.324132181 Testing: 5 clients with 50 transactions (5 samples) TPS Min 915.127422; Max: 1042.774615; Avg: 987.7306472; Dev: 47.8906556447 CTPS Min: 951.916780; Max: 1090.940827; Avg: 1030.9061366; Dev: 52.042571476 Testing: 10 clients with 5 transactions (5 samples) TPS Min 598.802395; Max: 720.990930; Avg: 677.7113778; Dev: 48.4403117914 CTPS Min: 797.143039; Max: 1033.399471; Avg: 944.3966004; Dev: 90.8906145724 Testing: 10 clients with 10 transactions (5 samples) TPS Min 665.552975; Max: 935.007620; Avg: 799.1810664; Dev: 126.329870233 CTPS Min: 774.287462; Max: 1157.742402; Avg: 964.7010562; Dev: 179.888588017 Testing: 10 clients with 50 transactions (5 samples) TPS Min 26.782745; Max: 1040.286120; Avg: 549.5094934; Dev: 486.121295095 CTPS Min: 26.812935; Max: 1086.554969; Avg: 570.8430692; Dev: 506.835600619 pg_benchmark executed 30 tests in about 39 seconds


### Exemple après optimisation

Shared Memory Max is 3,072.00Mb PostgreSQL is using 10.57Mb Testing: 5 clients with 5 transactions (5 samples) TPS Min 668.878425; Max: 716.147698; Avg: 692.1704534; Dev: 18.0051424274 CTPS Min: 958.662474; Max: 1050.199538; Avg: 1002.8300562; Dev: 34.217782456 Testing: 5 clients with 10 transactions (5 samples) TPS Min 793.550025; Max: 989.922588; Avg: 909.7281046; Dev: 73.6959734873 CTPS Min: 974.601875; Max: 1265.086152; Avg: 1146.0283372; Dev: 112.187073149 Testing: 5 clients with 50 transactions (5 samples) TPS Min 1051.069358; Max: 1226.031092; Avg: 1126.4953152; Dev: 63.4721625573 CTPS Min: 1105.519638; Max: 1297.420728; Avg: 1186.3068702; Dev: 69.9807788556 Testing: 10 clients with 5 transactions (5 samples) TPS Min 622.091721; Max: 781.677480; Avg: 729.4070334; Dev: 64.9725527355 CTPS Min: 846.596681; Max: 1169.235086; Avg: 1062.2111624; Dev: 131.355197786 Testing: 10 clients with 10 transactions (5 samples) TPS Min 945.930607; Max: 1084.363479; Avg: 997.8187306; Dev: 57.4096186704 CTPS Min: 1182.886005; Max: 1401.993635; Avg: 1264.4000858; Dev: 91.9101739593 Testing: 10 clients with 50 transactions (5 samples) TPS Min 1210.334319; Max: 1467.351431; Avg: 1341.577537; Dev: 98.2366159289 CTPS Min: 1277.331705; Max: 1563.379401; Avg: 1422.1924606; Dev: 109.288692112 pg_benchmark executed 30 tests in about 5 seconds



## Outils

### PgBouncer

[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).

Dans une infrastructure multi-serveurs, il peut être installé soit sur chacun des frontaux web à la manière d'un HAProxy, soit sur les serveurs de bases de données dépendamment de ce que l'on recherche.

Installation :

apt install pgbouncer


Augmentation du nombre de fichiers ouverts maximum :

echo "ulimit -n 65536" >>/etc/default/pgbouncer


> *Note* : en Stretch PgBouncer n'a toujours pas d'unité [systemd](HowtoSystemd) donc le fichier `/etc/default/pgbouncer` est toujours pris en compte.

La configuration se fait ensuite dans le fichier `/etc/pgbouncer/pgbouncer.ini` :

[databases]

La base foodb est héberger sur le serveur accessible sur 10.0.0.32

foodb = host=10.0.0.32 […]

[pgbouncer]

À décommenter si pgbouncer doit être accessible depuis d'autres machines

#listen_addr = *

Utilisateurs qui auront accès à la pseudo-base pgbouncer

admin_users = pgbpostgres stats_users = pgbstats

La connexion au serveur redevient libre lorsque le client termine une transaction

Autres valeurs possibles : session (lorsque le client ferme la session), statement (lorsque la requête se termine)

pool_mode = transaction

Nombre maximum de connexions entrantes

max_client_conn = 5000

Nombre de connexion maintenues avec le serveur

default_pool_size = 20

log_connections = 0 log_disconnections = 0


Il est également nécessaire de lister les utilisateurs pouvant se connecter, puisque l'authentification se fera désormais au niveau de PgBouncer. Dans le fichier `/etc/pgbouncer/userlist.txt` :

"pgbpostgres" "some_password" "pgbstats" "some_password" "jdoe" "some_password" […]


Une fois démarré, PgBouncer écoute sur le port TCP/6432.

Une pseudo-base spécifique à PgBouncer permet de faire des opérations d'administration et de récupérer des statistiques intéressantes :

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

* Documentation : <http://docs.pgbarman.org/release/2.4/>

[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 un serveur de sauvegardes :

apt install barman barman-cli


Le client PostgreSQL sur le serveur Barman doit être disponible dans la même version que sur le serveur PostgreSQL, exemple pour un serveur en 9.2 :

apt install postgresql-client-9.2


Pour chaque serveur PostgreSQL à sauvegarder, on crée un fichier dans `/etc/barman.d/` :

[foo] description = "foo.example.net 9.6/main PostgreSQL instance"

ssh_command = ssh postgres@foo.example.net conninfo = host=foo.example.net user=barman dbname=postgres

backup_method = rsync reuse_backup = link

archiver = on

last_backup_maximum_age = 1 DAY


Il faut ensuite autoriser barman à se connecter en SSH et PostgreSQL sur la machine à sauvegarder.

Dans la configuration de PostgreSQL, rajouter les directives suivantes :

wal_level = 'replica' # ou plus haut archive_mode = on archive_command = 'rsync -a %p barman@foo.example.com:foo/incoming/%f'


Une fois en place, voici quelques commandes utilisables :

barman list-server

foo00 - foo00 9.2/main PostgreSQL instance

barman status foo00

Server foo00: Description: foo00 9.2/main PostgreSQL instance Active: True Disabled: False PostgreSQL version: 9.2.24 Cluster state: in production pgespresso extension: Not available Current data size: 287.8 GiB PostgreSQL Data directory: /var/lib/postgresql/9.2/main Current WAL segment: 0000000100002CEF00000083 PostgreSQL 'archive_command' setting: rsync -a %p barman@foo00.example.com:/var/lib/barman/foo00/incoming/%f Last archived WAL: No WAL segment shipped yet Retention policies: not enforced No. of available backups: 1 First available backup: 20180723T174316 Last available backup: 20180723T174316 Minimum redundancy requirements: satisfied (1/0)

barman list-backup foo

foo00 20180723T174316 - Mon Jul 23 21:47:34 2018 - Size: 413.2 GiB - WAL Size: 89.9 GiB



### pgbadger

[PgBadger](https://github.com/dalibo/pgbadger) permet d'analyser des logs PostgreSQL et de générer une page HTML représentant les résultats sous forme de graphes et tableau de données.

Avant tout, il faut définir le `log_min_duration_statement` dans la configuration de PostgreSQL à une valeur raisonnable (suffisamment de requêtes doivent être loguées sans pour autant impacter les performances). S'assurer également que PostgreSQL ajoute suffisamment d'information de contexte sur chaque ligne de log écrite : `log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d '`. On peut également loguer des informations supplémentaires comme les connexions/déconnexions, fichiers temporaires, etc…

En résumé voici ce à quoi la configuration de PostgreSQL devrait ressembler :

log_min_duration_statement = 200 // 200 ms log_checkpoints = on log_connections = on log_disconnections = on log_lock_waits = on log_temp_files = 0 log_autovacuum_min_duration = 0 log_error_verbosity = default log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d ' lc_messages='C'


Installation :

apt install pgbadger


Exécution :

/usr/bin/pgbadger -I -q /var/log/postgresql/postgresql.log.1 -O /var/www/pg_reports/


Plusieurs autres options peuvent être spécifiées, voir l'aide de la commande pour plus d'informations.

On peux mettre l'analyse des logs avec pgBadger dans la crontab comme ceci, avec un umask pour avoir les droits corrects pour nginx ou Apache :

0 4 * * * umask 022 ; /usr/bin/pgbadger -I -q /var/log/postgresql/postgresql-9.6-main.log.1 -O /var/www/pg_reports/


Vérifier aussi le logrotate des logs de posgresql, si on veux avoir un rapport journalier ou hebdomadaire.

### phpPgAdmin / pgAdmin III

phpPgAdmin et pgAdmin III sont des clients web (pour le premier) et lourd (pour le second) pour interagir avec des bases de données PostgreSQL.

Installation :

apt install phppgadmin

apt install pgadmin3



### pg_stat_statements

`pg_stat_statements` est une extension PostgreSQL permettant de collecter des statistiques sur les requêtes reçues.

Installation :

apt install postgresql-contrib-9.6


Activation :

shared_preload_libraries = 'pg_stat_statements'


On a maintenant accès à une vue contenant des informations utiles :

bench=# SELECT query, calls, total_time, rows, 100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;



## Activation du support des larges pages (Hugepages)

Le support des Hugepages permet une optimisation de postgresql et du kernel, pour les requêtes avec un besoin important de mémoire.

Dans un premier temps il faut vérifier si le kernel supporte les Hugepages :

cat /proc/meminfo | grep -i huge

AnonHugePages: 198656 kB ShmemHugePages: 0 kB HugePages_Total: 0 HugePages_Free: 85 HugePages_Rsvd: 16 HugePages_Surp: 0 Hugepagesize: 2048 kB


On voit que la taille d'une page est défini à 2048kB (2MB), mais que le nombre total des Hugepages (HugePages_Total) est à 0, les Hugepages sont donc désactivé pour le moment.

La définition du nombre total des Hugepages se défini avec la variable `vm.nr_hugepages`, pour savoir quelle valeur on doit définir à cette variable, il faut récupéré la valeur `VmPeak` du processus de PostgreSQL et de divisé cette valeur par la valeur du `Hugepagesize` :

grep ^VmPeak /proc/1971/status

VmPeak: 17356400 kB


On divise donc VmPeak/Hugepagesize (17356400/2048=8474) ce qui donne la valeur de 8474 pour `vm.nr_hugepages`.

On peux utiliser ce script qui permet d'une manière plus rapide d'avoir cette information :

#!/bin/bash pid=head -1 /var/lib/postgresql/9.6/main/postmaster.pid echo "Pid: $pid" peak=grep ^VmPeak /proc/$pid/status | awk '{ print $2 }' echo "VmPeak: $peak kB" hps=grep ^Hugepagesize /proc/meminfo | awk '{ print $2 }' echo "Hugepagesize: $hps kB" hp=$((peak/hps)) echo Set Huge Pages: $hp


On défini donc vm.nr_hugepages à 8474 :

sysctl -w vm.nr_hugepages=8474


Il faut ensuite définir un ulimit illimité pour les memlock, on défini les memlock en unlimited si c'est un serveur PostgeSQL dédié comme ceci :

vim /etc/security/limits.conf

  •  soft    memlock         unlimited
    
  •  hard    memlock         unlimited
    

Si le serveur, n'est pas un serveur SQL dédié, on créer un groupe `hugepages` et on ajoute l'utilisateur postgres à ce groupe :

groupadd hugepages

gpasswd -a postgres hugepages


Et on ajoute le groupe hugepages dans le fichier `/etc/security/limits.conf` :

vim /etc/security/limits.conf

@hugepages soft memlock unlimited @hugepages hard memlock unlimited


On spécifie l'ID du groupe pour la variable `vm.hugetlb_shm_group`, il faut donc définir soit l'ID du groupe hugepages, soit l'ID du groupe postgres selon la configuration choisi :

sysctl -w vm.hugetlb_shm_group=119


On peux ensuite rendre la configuration persistante en créant le fichier `/etc/sysctl.d/z-pgsql-tuning.conf` :

vim /etc/sysctl.d/z-pgsql-tuning.conf

vm.nr_hugepages = 8474 vm.hugetlb_shm_group = 119


On vérifie que l'utilisateur postgres est bien illimité pour les memlock (max locked memory) :

postgres@exotismes-sql10:~$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 513312 max locked memory (kbytes, -l) unlimited max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 513312 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited


Et enfin, on active la variable huge_page dans `/etc/postgresql/9.6/main/postgresql.conf` :

vim /etc/postgresql/9.6/main/postgresql.conf

huge_pages = on


## Réplication

Plusieurs solutions de réplication plus ou moins avancées existent avec PostgreSQL :


* _Streaming Replication_ ou _Physique_ : 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*
* _Logique_ : les données sont répliquées au niveau des objets par un système de publication/abonnement
* _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ées 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é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.
* _Slony_ : système de réplication basé sur l'ajout de triggers sur chaque table à répliquer. Cela nécessite une gestion assez complexe mais c'était la seule façon d'avoir une réplication immédiate avant l'arrivée de la _Streaming Replication_. Cela reste la seule solution pour avoir une réplication au niveau des tables et non de la base entière (par exemple si vous voulez répliquer une table d'un serveur A vers un serveur B, et répliquer une autre table du serveur B vers A).

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 disponibilité, PostgreSQL a [une page sur cela dans leur documentation](https://www.postgresql.org/docs/current/static/different-replication-solutions.html).

> *Note* : l'expédition des logs entre des serveurs pgsql nécessite qu'ils soient à la même version majeure.


### Streaming Réplication

Voir [/HowtoPostgreSQL/ReplicationPhysique]().

### Réplication Logique

Voir [/HowtoPostgreSQL/ReplicationLogique]().

### Slony

Voir [/HowtoPostgreSQL/Slony]().

## Utilisation

Voir [/HowtoPostgreSQL/Utilisation]().

## FAQ

### J'ai lancé `pg_top` mais je n'ai aucun résultat

Vous utilisez une version non compatible avec votre base, essayez avec une version du paquet `ptop`.

### Rendre PostgreSQL moins susceptible d'être killer par l'OMkiller

Voici une unité systemd avec des ajustements sur les variables d'environnement pour rendre PostgreSQL moins killable par l'OMkiller

systemd service for managing all PostgreSQL clusters on the system. This

service is actually a systemd target, but we are using a service since

targets cannot be reloaded.

[Unit] Description=PostgreSQL RDBMS

[Service] OOMScoreAdjust=-1000 Environment=PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj Environment=PG_MASTER_OOM_SCORE_ADJ=-1000 Environment=PG_CHILD_OOM_SCORE_ADJ=0 Type=oneshot ExecStart=/bin/true ExecReload=/bin/true RemainAfterExit=on

[Install] WantedBy=multi-user.target


### Activer module `pgcrypto`

Dans le *shell* `psql` :

hostname=# drop extension pgcrypto; hostname=# create extension pgcrypto;


Pour vérifier :

hostname=# select pg_get_functiondef(to_regproc('gen_random_uuid')); pg_get_functiondef

CREATE OR REPLACE FUNCTION public.gen_random_uuid() + RETURNS uuid + LANGUAGE c + PARALLEL SAFE + AS '$libdir/pgcrypto', $function$pg_random_uuid$function$+

(1 row)