Anslutningspoolning är ett enkelt men effektivt sätt att förbättra prestandan för dina appar och minska belastningen på dina PostgreSQL-servrar. Läs vidare för att lära dig mer om hur du använder PgBouncer för att slå samman PostgreSQL-anslutningar.
Varför Connection Pooling?
PostgreSQL har en ganska tung anslutningshanteringsarkitektur. För varje inkommande anslutning, postmästaren (den huvudsakliga Postgres-demonen) delar ut en ny process (vanligtvis kallad en backend ) för att hantera det. Även om denna design ger bättre stabilitet och isolering, gör den den inte särskilt effektiv vid hantering av kortlivade anslutningar. En ny Postgres-klientkoppling involverar TCP-installation, processskapande och backend-initiering – allt detta är kostsamt när det gäller tid och systemresurser.
Detta är naturligtvis bara ett problem om anslutningar skapas för ofta och kasseras utan återanvändning. Tyvärr är det inte ovanligt att ha ett kluster av webbnoder som kör applikationer skrivna i PHP eller andra sådana språk som måste ansluta till databasen en gång per sidladdning. Batchjobb som snabbt gör en mängd anslutningar i snabb följd är också vanliga. Använder connectionpooling i sådana scenarier kan drastiskt minska belastningen på din PostgreSQLserver och dramatiskt förbättra frågefördröjningen.
Med anslutningspooling ansluter klienterna till en proxyserver som upprätthåller en uppsättning direktanslutningar till den riktiga PostgreSQL-servern. Vanligtvis inser (och bör inte) klienterna att de är anslutna till en proxyserver snarare än den faktiska servern. Proxyn kan köras på samma nod som klienten (exempelvis på varje webbnod), i vilket fall klienterna kan ansluta till proxyn via Unix-domänsockets som har mycket låga anslutningskostnader. Även om proxyn är på en annan nod och klienten behöver en TCP-anslutning för att nå proxyn, kan overheaden för en ny Postgres-backend undvikas.
Vad är PgBouncer?
PgBouncer är en öppen källkod, lätt, enkelbinär anslutningspoolare för PostgreSQL. Den kan slå samman anslutningar till en eller flera databaser (på möjligen olika servrar) och betjäna klienter över TCP- och Unix-domänsockets.
PgBouncer upprätthåller en pool av anslutningar för varje unik användare, databaspar. Den är vanligtvis konfigurerad för att dela ut en av dessa anslutningar till en ny inkommande klientanslutning och returnera den tillbaka till poolen när klienten kopplar bort. Du kan konfigurera PgBouncer att poola mer aggressivt, så att den kan hämta och returnera anslutningen till poolen vid transaktions- eller uttalandegränser snarare än anslutningsgränser. Det finns dock några potentiellt oönskade konsekvenser av dessa.
Du bör kunna installera PgBouncer med din distros pakethanterare:
# RedHat/CentOS/..
$ sudo yum install pgbouncer
# Debian/Ubuntu/..
$ sudo apt-get install pgbouncer
Det är också tillgängligt från standard Postgres APT och YUM-repos, som kan användas om din distros paket är gamla eller trasiga.
PgBouncer förlitar sig på en huvudkonfigurationsfil, vanligtvis lagrad som/etc/pgbouncer/pgbouncer.ini
. Du kan anropa pgbouncer som en systemd tjänst, eller helt enkelt köra den även utan superanvändarprivilegier med sökvägen till denna konfigurationsfil.
För att ge det en snurr, låt oss skapa en databas db1 och en användare user1 på vår 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 kommer att ansluta till databasen db1
med användarnamnet user1
andlösenord user1pass
. Vårt mål är att få klienterna att ansluta till PgBouncer som kommer att proxy och poola anslutningarna till den faktiska servern.
Låt oss nu skapa en fil (var som helst) med detta innehåll:
[databases]
db1 = host=localhost dbname=db1
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 16432
auth_file = userlist.txt
Vi måste också skapa en "userlist.txt"-fil i samma katalog, med användarnamnet och (hashade) lösenord för användare som PgBouncer tillåter att ansluta. Skapa "userlist.txt" med följande innehåll:
"user1" "md5638b81c77071ea624d1ad4adb1433540"
Det andra värdet är MD5 för "user1passuser1", med prefixet "md5". Detta är den vanliga Postgres-konventionen.
Låt oss nu starta PgBouncer i förgrunden:
$ /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 startat en PgBouncer som lyssnar på 127.0.0.1 TCP-port 16432, samt på Unix-domänsocket /tmp/.s.PGSQL.16432
. Den enda "databas" som är tillgänglig på denna proxyserver är db1
. Den enda användare som kan ansluta till denna server är user1
. Låt oss försöka ansluta 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) ansluter framgångsrikt till localhost:16432, men du kan se att anslutningen faktiskt proxias till localhost:5432.
Du kan försöka koppla från och ansluta igen några gånger och sedan kontrollera hur många anslutningar som fortfarande finns på den faktiska servern:
postgres=# select count(*) from pg_stat_activity
postgres-# where datname='db1' and usename='user1';
count
-------
1
(1 row)
PgBouncer kommer inte att koppla från den faktiska anslutningen när klienten kopplar ur. Du kan konfigurera de minsta, maximala och reserverade anslutningarna som PgBouncer kommer att upprätthålla för varje pool i konfigurationsfilen.
Distribuera PgBouncer
Var installerar och kör du PgBouncer? Det finns olika svar, med olika fördelar:
- På Postgres-servernoden :Du kan installera den bredvid själva PostgreSQLservern, på samma nod. Klienterna ansluter till PgBouncer-porten istället för Postgres-porten. Detta har effekten av en "förbättrad" Postgres som gör anslutningspooling internt. Du behöver också bara underhålla en kopia av konfigurationsfilerna för PgBouncer. Å andra sidan innebär detta faktiskt att köra något annat också på PostgreSQL-servernoden, vilket kanske inte är lätt eller tillåtet (brandväggar, policyer) eller ens möjligt (AWSRDS).
- På klientnoder :Du kan installera PgBouncer i varje klientnod, till exempel kör varje webbnod Apache och PHP, och PHP-skripten ansluter till den lokala PgBouncer. Detta har fördelen av att inte behöva störa serverkonfigurationen, och poolkonfigurationen kan användas för att hålla serverbelastningen förutsägbar. Å andra sidan, om antalet klientnoder är enormt, eller kan variera mycket beroende på belastningen/ trafik, kan servern snabbt överbelastas.
- Som ett fristående kluster :Det tredje alternativet att ha ett kluster av oberoende, statslösa PgBouncer-noder, frontade av en TCP-lastbalanserare som HAProxy. Denna installation är mer komplicerad än de andra två alternativen, men ger maximal kontroll och konfigurerbarhet.
Administration
PgBouncer låter användare markerade som administratörer ansluta till en virtuell databas som kallas "pgbouncer" och utfärda kommandon för att kontrollera servern och se statistik. För att prova detta, låt oss först markera "user1" som en administratör genom att ändra 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 användare1 ansluta till databasen som heter "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=#
Härifrån kan du göra olika saker som att aktivera eller inaktivera en viss databas, inspektera och ladda om konfigurationen och mer:
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)
Övervakning
Det finns också kommandon för att visa olika statistik om PgBouncer, inklusive:
- Statistik per databas om sökfrågans varaktighet, klientens väntetid, nätverksanvändning, transaktionsantal
- Statistik per pool om antalet aktiva och väntande klienter, lediga och använda serveranslutningar
Statistik hämtas med "SHOW xyz"-kommandon, som den här för att hämta poolrelaterad 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
Ytterligare läsning
PgBouncers hemsida har mer information om alla de olika funktionerna och konfigurationsalternativen för PgBouncer.
- PgBouncers hemsida
- PgBouncer GitHub Repository
- Postgres Wiki har information om anslutningspoolning
- Pgpool är ett annat alternativ för anslutningspoolning