[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.
> *Note* : il faut s'assurer d'avoir configuré sa locale système `dpkg-reconfigure locales` avant installation car l'initialisation des bases de données est faite avec la locale du système.
Le dépôt **apt.postgresql.org** permet d'installer des versions différentes de PostgreSQL, ainsi que plusieurs extensions. C'est maintenu par le *PostgreSQL Global Development Group (PGDG)* qui rassemble plusieurs développeurs Debian.
La plupart des changements de configuration peuvent être pris en compte à chaud avec un `reload`.
On verra dans ce cas les changements dans les logs:
~~~
2016-12-20 17:02:58 CET [13555]: [2-1] user=,db= LOG: received SIGHUP, reloading configuration files
2016-12-20 17:02:58 CET [13555]: [3-1] user=,db= LOG: parameter "log_temp_files" changed to "6MB"
~~~
## Instances PostgreSQL
Une surcouche Debian permet de gérer simplement plusieurs versions et plusieurs instances d'une même version de PostgreSQL. Cela permet entre autre de faciliter les migrations d'une version majeure à une autre.
Le port d'écoute de postgres sera automatiquement incrémenté de 1 pour ne pas rentrer en conflit avec les autres instances. L'encodage utilisé par défaut pour l'instance est celui du système. Par exemple si le système est en en_US.UTF-8 par défaut, l'instance créée utilisera l'encodage en_US.UTF-8.
On peut remarquer que toute l'arborescence est organisée en fonction des versions et des instances. Cela permet de les rendre réellement indépendantes :
PostgreSQL permet de lier un utilisateur Unix à un utilisateur PostgreSQL. C'est le cas pour l'utilisateur *postgres* (superadmin PostgreSQL), qui est lié à l'utilisateur Unix *postgres*.
À noter que l'utilisateur Unix *postgres* a tous les droits sur les fichiers de configuration, les journaux, etc… sur le système.
Par défaut, un utilisateur Unix *foo* sera automatiquement lié à son compte PostgreSQL *foo* si il existe. C'est très pratique dans le cas de multiples comptes web sur la machine, on évite ainsi de stocker un mot de passe dans un fichier de paramètre de connexion.
La gestion des permissions se fait dans le fichier de configuration `pg_hba.conf` de l'instance en question.
Si on préfère passer par un mot de passe pour s'authentifier, il faut changer la ligne suivante dans le fichier `pg_hba.conf` :
La configuration par défaut est faite pour s'adapter à toutes sortes de machines, elle n'est donc pas adaptée en terme de performances. Nous allons voir ici quelques paramètres qui peuvent améliorer les performances de PostgreSQL. Vous pouvez utilisez le site [PgTune](http://pgtune.leopard.in.ua/) pour avoir une idée des paramètres à utiliser en fonction de vos ressources.
* **shared_buffers** : Ce paramètre détermine la quantité de mémoire dédiée à PostgreSQL pour mettre en cache les données. Il est recommandé de mettre **1/4 de la quantité de RAM total** de votre machine.
* **max_connections** : Détermine le nombre maximum de clients connectés simultanément. Ce paramètre est très important pour certains paramètres car les ressources mémoires qui sont ou seront allouées par client, il est donc suggérer d'utiliser le maximum de mémoire possible. work_mem x nb_clients = maxRAM. Éviter de dépasser les 300. Dans de tel cas il faut préférer l'utilisation d'un multiplexeur de connexions comme PgBouncer.
* **work_mem** : Le work_mem permet d'éviter lors de grosses opérations, de swapper les données sur le disque pour les traiter. Attention il est rattaché au nombres de clients ! Par exemple un work_mem de 50Mo avec 30 utilisateurs, cela donne 1.5Go, il est donc facile de faire passer la machine en out-of-memory… On le définit généralement à **8 voir 16 Mo**.
* **effective_cache_size** : Ce paramètre détermine combien de mémoire devrait être disponible pour l'OS et les mémoire tampons de PostgreSQL, ce n'est pas une allocation. Cette valeur est seulement utilisé par le planificateur de requêtes de PostgreSQL pour savoir si les requêtes vont tenir en RAM ou pas. Il est recommandé de mettre **75% de la RAM** pour un serveur dédié à PostgreSQL.
* **checkpoint_segments** : PostgreSQL (dans les versions antérieures à 9.6) écrit les transactions effectuées dans un fichier de log (nommé _WAL_, pour _Write Ahead Log_). Un segment est en fait un fichier, qui fait 16Mo par défaut. Un checkpoint est fait à chaque _checkpoint_segment_, par défaut 3, donc 3x16 = 48Mo, ce qui peut être un goulot d'étranglement de faire des checkpoints tous les 48Mo. Il est recommandé de le mettre **entre 8 (faible écritures sur les bases) et 64 (beaucoup d'écritures)**. 30 est une bonne valeur pour un usage standard.
* **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 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.
Comme pour les instances, par défaut la base sera créée avec l'encodage du système. Si le système est en UTF-8 par défaut, la base créée sera en UTF-8. On peut spécifier un encodage alternatif avec l'option `-E`.
* sauvegarde SQL: simple à mettre en place, mais lent, impacte les requêtes en cours (locks) et consomme beaucoup de place;
* sauvegarde du datadir: complètement transparent pour les connexions actives, synchro uniquement des fichiers modifiés par rapport à la dernière sauvegarde, mais plus complexe à mettre en place (gestion des WAL).
* À moins de vouloir restaurer le contenu sur un autre moteur de base de données, il est préférable de faire des dumps au format _custom_, qui permet la compression, un processus de restauration sur plusieurs threads, et de sélectionner finement quels éléments restaurer lors de la restauration:
**Important:** il est nécessaire de faire un `ANALYZE` après une restauration de tables ou bases de données afin de mettre à jour les statistiques de PostgreSQL. Ces statistiques sont utilisées par PostgreSQL pour déterminer la manière la plus optimum de requêter la base en fonction de la répartition des valeurs des champs.
* augmenter autant que possible le `_maintenance_work_mem_` (attention, il sera multiplié par le nombre de processus utilisés pour la restauration). Dans tous les cas, ne pas dépasser les 2Go. Peut être fait dans la conf ou dans une session;
* Mettre la directive `synchronous_commit` à `off`.
Ainsi, dès qu'un WAL est marqué comme complété (`pg_xlog/archive_status/*.ready`), il est copié sur le serveur de backup conformément à `archive_command`. Si la copie a réussi, le `.ready` est renommé en `.done`.
Attention à ne surtout pas effacer le contenu de `pg_xlog/`, qui peut contenir le dernier WAL courant (donc pas encore copié sur le serveur de backup).
PostgreSQL va rejouer tous les WAL, exactement de la même manière qu'il le fait en cas de crash, puis se mettra à accepter les connexions.
Il est possible de rejouer les WAL jusqu'à une certaine date (`recovery_target_time`) ou un certain identifiant de transaction (`recovery_target_xid`).
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…).
Dans la procédure suivante, on suppose que l'on met à jour un cluster en version 9.3 vers la version 9.6. Le cluster s'appelle *main* (nom par défaut).
* lancer la mise à jour du cluster. En résumé, `pg_upgrade` va interdire toute connexion (autre que postgres) sur le cluster à mettre à jour, faire un rsync du datadir (option `-m upgrade`) avec des hardlinks, pour sauver de la place et du temps (option `-k`) et intervertir le port de connexion des 2 clusters puis réautoriser les connexions au cluster:
* The Internals of PostgreSQL Chapter 9 WAL : <http://www.interdb.jp/pg/pgsql09.html>
* Article de Dalibo (ancien mais presque encore exact) : <https://www.dalibo.org/glmf108_postgresql_et_ses_journaux_de_transactions>
Une transaction est un ensemble de requêtes SQL qui sont jouées d'un seul bloc. Pour créer une transaction, on utilise BEGIN et COMMIT au début et à la fin du groupe d'instructions ; on peut aussi utiliser ROLLBACK pour annuler une transaction en cours.
En fait, PostgreSQL traite aussi les instructions SQL simples comme une transaction, et considére donc que tout ce qui est écrit est une transaction.
Quand une transaction est jouée, PostgreSQL ne l'écrit pas directement dans les fichiers de bases de données, il l'écrit d'abord dans un journal de transactions situé dans le répertoire `pg_xlog/` (ou `pg_wal/` depuis PostgreSQL 10) puis s'assure que l'OS l'écrit réellement sur le disque (sous Linux c'est en général avec la fonction système `fsync()`).
Les données sont écrites dans les fichiers de bases de données lors de checkpoints réguliers : si les journaux de transactions dépassent 48 Mo ou alors si cela fait plus de 5 minutes (ces valeurs sont réglages). C'est également fait lors d'un arrêt de PostgreSQL ou de l'exécution de la commande CHECKPOINT.
On appelle **WAL** (Write Ahead Logging) l'ensemble du mécanisme ou alors plus simplement les journaux de transaction.
Par défaut, il y a 8 fichiers de 16 Mo qui tournent et s'effacent tout seul :
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` 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 :
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 :
`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).
[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 :
# 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` :
[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.
[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 :
* _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.
* _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*
* _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 disoponibilite, PostgreSQL a [une page sur cela dans leur documentation](https://www.postgresql.org/docs/current/static/different-replication-solutions.html).