Le regroupement de connexions est un moyen simple mais efficace d'améliorer les performances de vos applications et de réduire la charge sur vos serveurs PostgreSQL. Lisez la suite pour en savoir plus sur l'utilisation de PgBouncer pour regrouper les connexions PostgreSQL.
Pourquoi le regroupement de connexions ?
PostgreSQL a une architecture de gestion des connexions plutôt lourde. Pour chaque connexion entrante, le postmaster (le principal démon Postgres) lance un nouveau processus (conventionnellement appelé backend ) pour le gérer. Bien que cette conception offre une meilleure stabilité et une meilleure isolation, elle ne la rend pas particulièrement efficace pour gérer les connexions de courte durée. Une nouvelle connexion client Postgres implique la configuration TCP, la création de processus et l'initialisation du backend, qui sont toutes coûteuses en termes de temps et de ressources système.
Bien sûr, ceci n'est un problème que si les connexions sont créées trop souvent et rejetées sans être réutilisées. Malheureusement, il n'est pas rare d'avoir un cluster de nœuds Web exécutant des applications écrites en PHP ou d'autres langages similaires qui doivent se connecter à la base de données une fois par chargement de page. Les travaux par lots qui établissent rapidement un tas de connexions en succession rapide sont également courants. Utilisation du regroupement de connexions dans de tels scénarios peut réduire considérablement la charge sur votre serveur PostgreSQL et améliorer considérablement les latences des requêtes.
Avec le regroupement de connexions, les clients se connectent à un serveur proxy qui maintient un ensemble de connexions directes au vrai serveur PostgreSQL. Généralement, les clients ne réalisent pas (et ne devraient pas) se rendre compte qu'ils sont connectés à un serveur proxy plutôt qu'au serveur réel. Le proxy peut s'exécuter sur le même nœud que le client (par exemple, sur chaque nœud Web), auquel cas les clients peuvent se connecter au proxy via des sockets de domaine Unix qui ont une surcharge de connexion très faible. Même si le proxy est sur un autre nœud et que le client a besoin d'une connexion TCP pour atteindre le proxy, la surcharge d'un nouveau backend Postgres peut être évitée.
Qu'est-ce que PgBouncer ?
PgBouncer est un pooler de connexions open-source, léger et à un seul binaire pour PostgreSQL. Il peut regrouper les connexions à une ou plusieurs bases de données (sur des serveurs éventuellement différents) et servir les clients via des sockets de domaine TCP et Unix.
PgBouncer maintient un pool de connexions pour chaque utilisateur unique, paire de bases de données. Il est généralement configuré pour distribuer l'une de ces connexions à une nouvelle connexion client entrante et la renvoyer dans le pool lorsque le client se déconnecte. Vous pouvez configurer PgBouncer pour qu'il se regroupe de manière plus agressive, afin qu'il puisse récupérer et renvoyer la connexion au pool aux limites de transaction ou d'instruction plutôt qu'aux limites de connexion. Il y a cependant des conséquences potentiellement indésirables à ceux-ci.
Vous devriez pouvoir installer PgBouncer avec le gestionnaire de paquets de votre distribution :
# RedHat/CentOS/..
$ sudo yum install pgbouncer
# Debian/Ubuntu/..
$ sudo apt-get install pgbouncer
Il est également disponible à partir des référentiels Postgres APT et YUM standard, qui peuvent être utilisés si les packages de votre distribution sont anciens ou cassés.
PgBouncer s'appuie sur un fichier de configuration principal, généralement stocké sous /etc/pgbouncer/pgbouncer.ini
. Vous pouvez invoquer pgbouncer en tant que service systemd ou simplement l'exécuter même sans privilèges de superutilisateur avec le chemin d'accès à ce fichier de configuration.
Pour essayer, créons une base de données db1 et un utilisateur user1 sur notre serveur :
$ sudo -u postgres psql
psql (10.6 (Debian 10.6-1.pgdg90+1))
Type "help" for help.
postgres=# create user user1 password 'user1pass';
CREATE ROLE
postgres=# create database db1 owner user1;
CREATE DATABASE
postgres=#
Les clients se connecteront à la base de données db1
avec le nom d'utilisateur user1
andpassword user1pass
. Notre objectif est d'amener les clients à se connecter à PgBouncer, qui proxy et regroupera les connexions au serveur réel.
Créons maintenant un fichier (n'importe où) avec ce contenu :
[databases]
db1 = host=localhost dbname=db1
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 16432
auth_file = userlist.txt
Nous devons également créer un fichier "userlist.txt" dans le même répertoire, avec le nom d'utilisateur et les mots de passe (hachés) des utilisateurs que PgBouncer permettra de se connecter.Créez "userlist.txt" avec le contenu suivant :
"user1" "md5638b81c77071ea624d1ad4adb1433540"
La deuxième valeur est le MD5 de "user1passuser1", préfixé par "md5". C'est la convention habituelle de Postgres.
Lançons maintenant PgBouncer au premier plan :
$ /usr/sbin/pgbouncer pgbouncer.ini
2019-02-05 11:46:18.011 10033 LOG file descriptor limit: 1024 (H:1048576), max_client_conn: 100, max fds possible: 130
2019-02-05 11:46:18.012 10033 LOG listening on 127.0.0.1:16432
2019-02-05 11:46:18.013 10033 LOG listening on unix:/tmp/.s.PGSQL.16432
2019-02-05 11:46:18.014 10033 LOG process up: pgbouncer 1.9.0, libevent 2.0.21-stable (epoll), adns: c-ares 1.12.0, tls: OpenSSL 1.1.0j 20 Nov 2018
Nous avons maintenant démarré un PgBouncer qui écoute sur le port TCP 127.0.0.1 16432, ainsi que sur le socket de domaine Unix /tmp/.s.PGSQL.16432
. La seule "base de données" disponible sur ce serveur proxy est db1
. Le seul utilisateur pouvant se connecter à ce serveur est user1
. Essayons de nous connecter avec psql
:
$ psql -U user1 -p 16432 -h localhost db1
Password for user user1:
psql (10.6 (Debian 10.6-1.pgdg90+1))
Type "help" for help.
db1=> select inet_server_addr(), inet_server_port();
inet_server_addr | inet_server_port
------------------+------------------
127.0.0.1 | 5432
(1 row)
db1=>
Le client (psql) se connecte avec succès à localhost :16432, mais vous pouvez voir que la connexion est en fait transmise par proxy à localhost :5432.
Vous pouvez essayer de vous déconnecter et de vous reconnecter plusieurs fois, puis de vérifier combien de connexions existent encore sur le serveur :
postgres=# select count(*) from pg_stat_activity
postgres-# where datname='db1' and usename='user1';
count
-------
1
(1 row)
PgBouncer ne déconnectera pas la connexion réelle lorsque le client se déconnecte. Vous pouvez configurer les connexions minimales, maximales et réservées que PgBouncer maintiendra pour chaque pool dans le fichier de configuration.
Déployer PgBouncer
Où installez-vous et exécutez-vous PgBouncer ? Il existe différentes réponses, avec différents avantages :
- Sur le nœud du serveur Postgres :Vous pouvez l'installer à côté du serveur PostgreSQL lui-même, sur le même nœud. Les clients se connectent au port PgBouncer plutôt qu'au port Postgres. Cela a l'effet d'un Postgres "amélioré" qui effectue la mise en commun des connexions en interne. Vous n'avez également qu'à conserver une copie des fichiers de configuration pour PgBouncer. D'un autre côté, cela implique en fait d'exécuter autre chose également sur le nœud du serveur PostgreSQL, ce qui peut ne pas être facile ou autorisé (pare-feu, politiques) ou même possible (AWSRDS).
- Sur les nœuds clients :Vous pouvez installer PgBouncer dans chaque nœud client, par exemple chaque nœud Web exécute Apache et PHP, et les scripts PHP se connectent au localPgBouncer. Cela présente l'avantage de ne pas avoir à perturber la configuration du serveur et la configuration du pool peut être utilisée pour maintenir la charge du serveur prévisible. D'un autre côté, si le nombre de nœuds clients est énorme ou peut varier considérablement en fonction de la charge trafic, le serveur peut être rapidement surchargé.
- En tant que cluster autonome :La troisième option pour avoir un cluster de nœuds PgBouncer indépendants et sans état, face à un équilibreur de charge TCP comme HAProxy. Cette configuration, tout en étant plus compliquée que les deux autres options, offre un contrôle et une configurabilité maximum.
Administration
PgBouncer permet aux utilisateurs marqués comme administrateurs de se connecter à une base de données virtuelle appelée "pgbouncer" et d'émettre des commandes pour contrôler le serveur et voir les statistiques. Pour essayer ceci, marquons d'abord "user1" en tant qu'administrateur en modifiant le fichier pgbouncer.ini :
[databases]
db1 = host=localhost dbname=db1
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 16432
auth_file = userlist.txt
admin_users = user1
Maintenant, l'utilisateur1 peut se connecter à la base de données nommée "pgbouncer":
$ psql -U user1 -p 16432 -h localhost pgbouncer
Password for user user1:
psql (10.6 (Debian 10.6-1.pgdg90+1), server 1.9.0/bouncer)
Type "help" for help.
pgbouncer=#
À partir de là, vous pouvez effectuer diverses actions, telles que l'activation ou la désactivation d'une base de données particulière, l'inspection et le rechargement de la configuration, etc. :
pgbouncer=# RELOAD;
RELOAD
pgbouncer=# DISABLE db1;
DISABLE
pgbouncer=# ENABLE db1;
ENABLE
pgbouncer=# SHOW FDS;
fd | task | user | database | addr | port | cancel | link | client_encoding | std_strings | datestyle | timezone | pa
----+--------+-------+----------+-----------+-------+----------------+------+-----------------+-------------+-----------+-----------+---
6 | pooler | | | 127.0.0.1 | 16432 | 0 | 0 | | | | |
7 | pooler | | | unix | 16432 | 0 | 0 | | | | |
9 | server | user1 | db1 | 127.0.0.1 | 5432 | 45404395804679 | 0 | UTF8 | on | ISO, MDY | localtime |
(3 rows)
Surveillance
Il existe également des commandes pour afficher diverses statistiques sur le PgBouncer, notamment :
- Statistiques par base de données sur la durée des requêtes, le temps d'attente des clients, l'utilisation du réseau, le nombre de transactions
- Statistiques par pool sur le nombre de clients actifs et en attente, les connexions serveur inactives et utilisées
Les statistiques sont récupérées avec des commandes de style "SHOW xyz", comme celle-ci pour récupérer les statistiques liées au pool :
pgbouncer=# SHOW POOLS;
-[ RECORD 1 ]---------
database | db1
user | user1
cl_active | 0
cl_waiting | 0
sv_active | 0
sv_idle | 0
sv_used | 1
sv_tested | 0
sv_login | 0
maxwait | 0
maxwait_us | 0
pool_mode | session
-[ RECORD 2 ]---------
database | pgbouncer
user | pgbouncer
cl_active | 1
cl_waiting | 0
sv_active | 0
sv_idle | 0
sv_used | 0
sv_tested | 0
sv_login | 0
maxwait | 0
maxwait_us | 0
pool_mode | statement
Lectures complémentaires
La page d'accueil de PgBouncer contient plus de détails sur toutes les différentes fonctionnalités et options de configuration de PgBouncer.
- Page d'accueil de PgBouncer
- Référentiel PgBouncer GitHub
- Postgres Wiki contient des informations sur le regroupement de connexions
- Pgpool est une autre option pour le regroupement de connexions