Forbindelsespooling er en enkel, men effektiv måde at forbedre ydeevnen for dine apps og mindske belastningen på dine PostgreSQL-servere. Læs videre for at lære mere om at bruge PgBouncer til at samle PostgreSQL-forbindelser.
Hvorfor Connection Pooling?
PostgreSQL har en ret tungvægts arkitektur for forbindelseshåndtering. For hver indkommende forbindelse, postmesteren (den vigtigste Postgres-dæmon) udskiller en ny proces (konventionelt kaldet en backend ) for at håndtere det. Selvom dette design giver bedre stabilitet og isolering, gør det det ikke særlig effektivt til at håndtere kortlivede forbindelser. En ny Postgres-klientforbindelse involverer TCP-opsætning, procesoprettelse og backend-initialisering – alt sammen dyre i form af tid og systemressourcer.
Dette er selvfølgelig kun et problem, hvis forbindelser oprettes for ofte og kasseres uden genbrug. Desværre er det ikke ualmindeligt at have en klynge af webnoder, der kører applikationer skrevet i PHP eller andre sådanne sprog, der skal oprette forbindelse til databasen én gang pr. sideindlæsning. Batchjob, der hurtigt laver en masse forbindelser i hurtig rækkefølge, er også almindelige. Anvender connectionpooling i sådanne scenarier kan drastisk reducere belastningen på din PostgreSQLserver og dramatisk forbedre forespørgselsforsinkelserne.
Med forbindelsespooling opretter klienterne forbindelse til en proxyserver, som opretholder et sæt direkte forbindelser til den rigtige PostgreSQL-server. Typisk indser (og bør) klienterne ikke, at de er forbundet til en proxyserver i stedet for den faktiske server. Proxyen kan køre på den samme node som klienten (f.eks. på hver webnode), i hvilket tilfælde klienterne kan oprette forbindelse til proxyen via Unix-domæne-sockets, som har meget lav forbindelsesoverhead. Selvom proxyen er på en anden node, og klienten har brug for en TCP-forbindelse for at nå proxyen, kan overhead af en ny Postgres-backend undgås.
Hvad er PgBouncer?
PgBouncer er en open source, let, enkelt-binær forbindelsespooler til PostgreSQL. Det kan samle forbindelser til en eller flere databaser (på muligvis forskellige servere) og betjene klienter via TCP- og Unix-domænesockets.
PgBouncer vedligeholder en pulje af forbindelser for hver unik bruger, databasepar. Det er typisk konfigureret til at udlevere en af disse forbindelser til en ny indgående klientforbindelse og returnere den tilbage til puljen, når klienten afbryder forbindelsen. Du kan konfigurere PgBouncer til at samle mere aggressivt, så den kan hente og returnere forbindelsen til puljen ved transaktions- eller statement-grænser i stedet for forbindelsesgrænser. Der er dog nogle potentielt uønskede konsekvenser af dem.
Du burde være i stand til at installere PgBouncer med din distros pakkehåndtering:
# RedHat/CentOS/..
$ sudo yum install pgbouncer
# Debian/Ubuntu/..
$ sudo apt-get install pgbouncer
Det er også tilgængeligt fra standard Postgres APT og YUM repos, som kan bruges, hvis din distros pakker er gamle eller ødelagte.
PgBouncer er afhængig af en hovedkonfigurationsfil, typisk gemt som/etc/pgbouncer/pgbouncer.ini
. Du kan påberåbe pgbouncer som en systemd service eller blot køre den selv uden superbrugerrettigheder med stien til denne konfigurationsfil.
For at give det en tur, lad os oprette en database db1 og en bruger bruger1 på vores server:
$ 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=#
Klienter vil oprette forbindelse til databasen db1
med brugernavnet user1
andpassword user1pass
. Vores mål er at få klienterne til at oprette forbindelse til PgBouncer, som vil proxy og samle forbindelserne til den faktiske server.
Lad os nu oprette en fil (hvor som helst) med dette indhold:
[databases]
db1 = host=localhost dbname=db1
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 16432
auth_file = userlist.txt
Vi er også nødt til at oprette en "userlist.txt"-fil i den samme mappe med brugernavnet og (hashed) adgangskoder for brugere, som PgBouncer tillader at oprette forbindelse til. Opret "userlist.txt" med følgende indhold:
"user1" "md5638b81c77071ea624d1ad4adb1433540"
Den anden værdi er MD5 for "user1passuser1", forankret med "md5". Dette er den sædvanlige Postgres-konvention.
Lad os nu starte PgBouncer i forgrunden:
$ /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
Vi har nu startet en PgBouncer, der lytter på 127.0.0.1 TCP-port 16432, samt på Unix-domæne-socket /tmp/.s.PGSQL.16432
. Den eneste "database", der er tilgængelig på denne proxyserver, er db1
. Den eneste bruger, der kan oprette forbindelse til denne server, er user1
. Lad os prøve at oprette forbindelse med 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=>
Klienten (psql) opretter forbindelse til localhost:16432, men du kan se, at forbindelsen faktisk bliver proxyet til localhost:5432.
Du kan prøve at afbryde og oprette forbindelse igen et par gange, og derefter kontrollere, hvor mange forbindelser der stadig findes på den faktiske server:
postgres=# select count(*) from pg_stat_activity
postgres-# where datname='db1' and usename='user1';
count
-------
1
(1 row)
PgBouncer vil ikke afbryde den faktiske forbindelse, når klienten afbryder forbindelsen. Du kan konfigurere minimum, maksimum og reserverede forbindelser, som PgBouncer vil opretholde for hver pulje i konfigurationsfilen.
Implementering af PgBouncer
Hvor installerer og kører du PgBouncer? Der er forskellige svar, med forskellige fordele:
- På Postgres-servernoden :Du kan installere det ved siden af selve PostgreSQLserveren på den samme node. Klienterne opretter forbindelse til PgBouncer-porten i stedet for Postgres-porten. Dette har effekten af en "forbedret" Postgres, som foretager forbindelsespooling internt. Du skal også kun vedligeholde én kopi af konfigurationsfilerne til PgBouncer. På den anden side involverer dette faktisk at køre noget andet også på PostgreSQL-servernoden, hvilket måske ikke er let eller tilladt (firewalls, politikker) eller endda muligt (AWSRDS).
- På klientknudepunkter :Du kan installere PgBouncer i hver klientnode, for eksempel kører hver webnode Apache og PHP, og PHP-scripts forbinder til den lokale PgBouncer. Dette har den fordel, at det ikke behøver at forstyrre serveropsætningen, og poolkonfigurationen kan bruges til at holde serverbelastningen forudsigelig. På den anden side, hvis antallet af klientnoder er enormt, eller kan variere meget afhængigt af belastningen/ trafik, kan serveren hurtigt blive overbelastet.
- Som en selvstændig klynge :Den tredje mulighed for at have en klynge af uafhængige, statsløse PgBouncer-noder, frontet af en TCP-belastningsbalancer som HAProxy. Selvom denne opsætning er mere kompliceret end de to andre muligheder, giver den maksimal kontrol og konfigurerbarhed.
Administration
PgBouncer giver brugere, der er markeret som administratorer, mulighed for at oprette forbindelse til en virtuel database kaldet "pgbouncer" og udstede kommandoer til at styre serveren og se statistik. For at prøve dette, lad os først markere "bruger1" som administrator ved at ændre filen 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
Nu kan bruger1 oprette forbindelse til databasen med navnet "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=#
Herfra kan du gøre forskellige ting som at aktivere eller deaktivere en bestemt database, inspicere og genindlæse konfigurationen og mere:
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)
Overvågning
Der er også kommandoer til at vise forskellige statistikker om PgBouncer, herunder:
- Statistik pr. database om forespørgselsvarighed, klientventetid, netværksbrug, transaktionsantal
- Per-pool-statistik om antallet af aktive og ventende klienter, inaktive og brugte serverforbindelser
Statistik hentes med "SHOW xyz"-stilkommandoer, som denne til fetchpool-relateret statistik:
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
Yderligere læsning
PgBouncer-hjemmesiden har flere detaljer om alle de forskellige funktioner og konfigurationsmuligheder i PgBouncer.
- PgBouncer-hjemmeside
- PgBouncer GitHub Repository
- Postgres Wiki har oplysninger om pooling af forbindelser
- Pgpool er en anden mulighed for forbindelsespooling