SSD Nodes Learn 🎉 VPS vanaf $5.50/mnd
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-21

PostgreSQL connection pooling op een VPS instellen

Voorkom dat uw PostgreSQL server crasht door een OOM killer. Ontdek waarom elke verbinding geheugen kost en hoe een pooler uw RAM verbruik op een kleine VPS stabiel houdt.

Waarom een kleine VPS zonder RAM komt te zitten voordat max_connections wordt bereikt

Connection pooling voor PostgreSQL op een VPS is geen truc voor snelheid. Het is de manier om een server met 4 GB RAM in de lucht te houden, omdat elke PostgreSQL-verbinding een apart besturingssysteemproces is dat eigen privégeheugen gebruikt. Een pooler plaatst een klein, vast aantal echte backend-processen achter een groot en goedkoop aantal clientverbindingen.

De standaardwaarde voor max_connections is 100. Dat is een limiet, geen budget. PostgreSQL controleert nooit of uw machine daadwerkelijk 100 backends kan draaien die gelijktijdig queries uitvoeren, waardoor de machine als eerste bezwijkt. De out of memory (OOM) killer van de kernel kiest een proces, en wanneer deze een backend kiest, herstart PostgreSQL het volledige cluster om het gedeelde geheugen weer veilig te stellen. Het logbestand toont server process (PID 1234) was terminated by signal 9: Killed, gevolgd door terminating any other active server processes. Elke open verbinding wordt verbroken, inclusief de gezonde verbindingen.

De server komt zonder geheugen te zitten omdat elke verbinding een proces is, en omdat work_mem per sorteer- of hash-operatie wordt toegewezen in plaats van per verbinding. Beide factoren versterken elkaar.

Elke verbinding is een proces, en elk proces kost geheugen

PostgreSQL gebruikt één proces per verbinding. De postmaster splitst (fork) een backend wanneer een client verbinding maakt, en die backend blijft actief totdat de client de verbinding verbreekt. Het is geen thread. Het heeft zijn eigen paginatabellen, zijn eigen catalogus-caches en zijn eigen gecachte queryplannen. Die caches groeien naarmate de verbinding meer tabellen raakt en meer verschillende query's uitvoert. Een langdurige verbinding in een drukke ORM-applicatie kost daarom meer dan een nieuwe verbinding.

Gedeeld geheugen is daadwerkelijk gedeeld. shared_buffers is één toewijzing voor het hele cluster, gemapt in elke backend. Privé-geheugen wordt niet gedeeld. Daarom is top hier misleidend: de resident set size (RSS) van een backend bevat de gedeelde pagina's die de backend heeft aangeraakt. Het optellen van de RSS van 50 backends telt shared_buffers dus 50 keer mee.

Meet in plaats daarvan het privégedeelte. PSS (proportional set size) deelt elke gedeelde pagina door het aantal processen dat deze mapt, en USS (unique set size) telt alleen de pagina's die uitsluitend bij dat proces horen.

sudo apt update
sudo apt install -y smem
sudo smem -k -P '^postgres'

De kolom USS is het geheugen dat vrijkomt als die backend wordt beëindigd. Dat zijn uw werkelijke kosten per verbinding. Gepubliceerde cijfers plaatsen een inactieve backend meestal in de enkele megabytes, en een backend die uitgebreide ORM-query's heeft uitgevoerd op een veelvoud daarvan. Beschouw dit als algemene cijfers, niet als uw eigen cijfers. Het enige cijfer waar u op kunt plannen, is het cijfer van uw eigen systeem onder uw eigen werklast.

Eén toewijzing per sessie wordt gemakkelijk over het hoofd gezien. temp_buffers staat standaard op 8MB en wordt per sessie toegewezen zodra die sessie voor het eerst een tijdelijke tabel aanraakt. Dit geheugen wordt pas teruggegeven wanneer de sessie eindigt.

work_mem wordt per operatie toegewezen, niet per verbinding

Hier gaat de berekening vaak mis. work_mem staat standaard op 4MB, en de documentatie van PostgreSQL is duidelijk over wat dat betekent: "een complexe query kan meerdere sorteer- en hash-operaties tegelijkertijd uitvoeren, waarbij elke operatie in de regel zoveel geheugen mag gebruiken als deze waarde aangeeft voordat er gegevens naar tijdelijke bestanden worden geschreven." Een queryplan met drie sorteerknooppunten kan binnen één backend op hetzelfde moment drie keer work_mem gebruiken.

Hash-operaties verbruiken meer. hash_mem_multiplier staat standaard op 2.0, dus een hash join of een hash aggregate kan work_mem maal twee gebruiken, wat bij standaardinstellingen neerkomt op 8MB. Parallelle queries vermenigvuldigen dit nogmaals, omdat elke parallelle worker een apart proces is met een eigen toewijzing.

Maak de berekening voor een 4 GB VPS. Stel shared_buffers in op 1 GB, laat work_mem op 4MB staan, en laat 100 verbindingen elk één query met twee hash-knooppunten uitvoeren. Dat is 100 maal 16MB, oftewel 1.6 GB aan privégeheugen bovenop de 1 GB aan shared buffers, nog voordat de page cache en andere processen op de server worden meegeteld. Verhoog nu work_mem naar 64MB omdat de server RAM over heeft; diezelfde 100 verbindingen verbruiken dan 100 maal 256MB. Er is geen waarschuwing. U merkt het pas wanneer de OOM killer ingrijpt.

U kunt controleren of work_mem te klein is in plaats van te gokken. Stel log_temp_files = 0 in binnen postgresql.conf en herlaad de configuratie. Elke keer dat er naar schijf wordt uitgeweken, wordt er een regel geschreven met de bestandsnaam en de grootte, zoals temporary file: path "base/pgsql_tmp/pgsql_tmp1234.0", size 20971520. Frequente spills betekenen dat een hogere work_mem zou helpen. Geen spills betekenen dat een verhoging niets oplevert en geheugen kost dat u niet heeft.

De pool-rekenkunde die voor problemen zorgt

Niemand is van plan om 240 verbindingen te openen. Men configureert een pool van 20 en draait de applicatie vervolgens op meer dan één plek.

ChartBackends requested per app topology, pool size 20 per worker (arithmetic)
The data behind this chart
[
  {
    "config": "1 worker",
    "backends": 20
  },
  {
    "config": "4 web workers",
    "backends": 80
  },
  {
    "config": "4 web + 2 background",
    "backends": 120
  },
  {
    "config": "2 hosts x 4 workers",
    "backends": 160
  },
  {
    "config": "3 hosts x 4 workers",
    "backends": 240
  }
]

Vier Gunicorn-workers, die elk een pool van 20 aanhouden, vragen om 80 backends. Voeg twee achtergrond-jobworkers toe en het totaal is 120. Schaal op naar 3 hosts x 4 workers en de applicatie vraagt om 240 backends tegenover een max_connections van 100. Geen van deze 5 opstellingen is op één enkel punt verkeerd geconfigureerd. De pool is per proces en geen enkel onderdeel van de applicatie kan het totaal overzien.

De standaardinstellingen van bibliotheken werken dit in de hand. SQLAlchemy's QueuePool staat standaard op pool_size=5 met max_overflow=10, dus 15 verbindingen per proces. HikariCP staat standaard op 10. Django had vóór 5.1 geen ingebouwde pool, slechts één verbinding per worker-proces. Daarom lopen Django-applicaties hier later tegenaan, en vervolgens direct volledig wanneer iemand CONN_MAX_AGE instelt of de nieuwere "pool": True-optie inschakelt. Als u een Django-app achter Gunicorn en nginx draait, is het getal waarmee u moet vermenigvuldigen uw aantal Gunicorn-workers, niet uw aantal servers.

Wat Postgres connection pooling op een VPS daadwerkelijk verandert

Een pooler is een proces dat het PostgreSQL wire protocol spreekt met uw applicatie aan de ene kant en een kleine set echte serververbindingen onderhoudt aan de andere kant. Het maakt queries niet sneller. Het verandert wie de kosten van een verbinding draagt en hoeveel echte backends er bestaan.

Twee zaken verbeteren. Het maken van een verbinding kost niet langer een fork plus de catalog-lookups die een lege backend-cache vullen, omdat de pooler zelf antwoordt op de connect-aanvraag van de client. Belangrijker nog is dat het aantal echte backends niet langer het aantal applicatieverbindingen volgt, waardoor 500 clients 20 backends kunnen delen.

Het wachten is de functie, en dit is het onderdeel waar mensen weerstand tegen bieden. Zonder pooler krijgen 500 gelijktijdige queries allemaal een backend en draaien ze allemaal tegelijk op twee CPU-cores, waardoor ze allemaal traag zijn en al het geheugen op hetzelfde moment wordt verbruikt. Met een pooler draaien er 20 en wachten de overige enkele milliseconden, waardoor elke actieve query een echt deel van de CPU krijgt en sneller klaar is. Een wachtrij voor een kleine pool is beter dan geen wachtrij voor een grote pool.

Wat een pooler niet doet, is andere zaken op de machine begrenzen. Als Postgres de VPS deelt met een app-server of met een vector database op dezelfde VPS, beschermt de pooler Postgres tegen uw applicatie en niets meer. Stel ook een hard plafond in voor de buren: u kunt het geheugen- en CPU-gebruik van een service beperken met systemd, zodat één op hol geslagen proces de database niet platlegt. Waar de database zelf draait, bepaalt hoe u deze limieten instelt; dat is het praktische verschil tussen Postgres draaien in Docker of direct op de host.

Session pooling versus transaction pooling

Eén instelling bepaalt alles, en dat is pool_mode.

Bij session pooling wordt een serververbinding toegewezen aan een client voor de gehele duur van die clientverbinding; de verbinding komt pas vrij wanneer de client de verbinding verbreekt. Alles werkt, omdat de pooler fungeert als een eenvoudige proxy. U bespaart enkel de kosten van het opzetten van de verbinding. Als de applicatie 200 verbindingen opent, heeft u nog steeds 200 backends nodig.

Bij transaction pooling wordt een serververbinding slechts voor de duur van één transactie aan een client toegewezen. Bij COMMIT of ROLLBACK keert de verbinding terug naar de pool en krijgt de volgende wachtende client deze toegewezen. Dit is wat 500 clients omzet in 20 backends. Dit is ook wat zaken kan verbreken, en dat gebeurt volgens ontwerp: uw volgende statement kan op een andere backend worden uitgevoerd dan uw vorige.

De standaardinstelling van PgBouncer is pool_mode = session. Installeer het, wijzig niets, en u krijgt het beperkte voordeel zonder de winst. Een derde modus, statement, geeft de verbinding na elk afzonderlijk statement terug en wijst transacties met meerdere statements af. Laat deze instelling ongemoeid, tenzij u precies weet waarom u deze nodig heeft.

Welke transactiemodus faalt, en waarom

Alles hieronder faalt om één reden. Het betreft status die in een enkele backend leeft, en transaction pooling garandeert niet dat u tweemaal dezelfde backend krijgt.

  • SET en RESET op sessieniveau. SET search_path, SET statement_timeout, SET TIME ZONE en SET ROLE komen terecht op de backend die dat statement afhandelde, en zijn verdwenen bij uw volgende transactie. Gebruik SET LOCAL binnen een expliciete transactie; deze is beperkt tot die transactie en is daarom veilig.
  • LISTEN. Het afleveren van notificaties hoort bij de backend die LISTEN uitvoerde, en die backend wordt aan een andere client gegeven zodra de transactie eindigt. NOTIFY werkt nog steeds in transactiemodus, wat dit een verwarrende fout maakt: het verzenden slaagt, het ontvangen gebeurt nooit. Als u LISTEN nodig heeft, open dan één extra verbinding rechtstreeks naar poort 5432 die de pooler omzeilt.
  • Advisory locks op sessieniveau. pg_advisory_lock() wordt vastgehouden door de sessie en vrijgegeven wanneer de sessie eindigt. Bij transaction pooling wordt uw unlock-aanroep uitgevoerd op een andere backend, waardoor de lock behouden blijft totdat PgBouncer die serververbinding beëindigt, wat standaard na server_lifetime, oftewel één uur, gebeurt. Gebruik pg_advisory_xact_lock(), die aan het einde van de transactie wordt vrijgegeven door dezelfde backend die hem heeft geplaatst.
  • PREPARE en DEALLOCATE, de SQL-statements. Nooit beschikbaar in transactiemodus.
  • WITH HOLD cursors, en elke server-side cursor waarvan wordt verwacht dat deze langer leeft dan de transactie.
  • Tijdelijke tabellen die een commit moeten overleven. CREATE TEMP TABLE ... ON COMMIT PRESERVE ROWS plaatst de tabel in het tijdelijke schema van één backend, en uw volgende transactie bevindt zich mogelijk niet in die backend.
  • LOAD.

Prepared statements op protocolniveau zijn het enige item dat is veranderd. PgBouncer 1.21.0 voegde ondersteuning hiervoor toe in transactiemodus, en 1.24.0 schakelde dit standaard in door max_prepared_statements op 200 in te stellen. Oudere builds laten dit op 0 staan, wat betekent dat het uitgeschakeld is. Ubuntu 24.04 levert PgBouncer 1.22.0, dus de functie is aanwezig, maar u moet max_prepared_statements zelf instellen. Als u niet zeker weet wat uw build doet, is de veilige instelling aan de clientzijde: psycopg 3 stopt met het gebruik van server-side prepared statements wanneer u prepare_threshold instelt op None.

Django benoemt zijn eigen versie hiervan. De documentatie stelt dat "het gebruik van een connection pooler in transaction pooling-modus (bijv. PgBouncer) het uitschakelen van server-side cursors voor die verbinding vereist", omdat "server-side cursors alleen toegankelijk zijn in de verbinding waarin ze zijn aangemaakt". Stel DISABLE_SERVER_SIDE_CURSORS in op True in de database-entry, anders wordt elke .iterator()-aanroep een intermitterende fout die alleen onder belasting optreedt.

Transactiemodus is de moeite waard, maar het is een contract. Lees de lijst, controleer uw ORM en uw bibliotheek voor achtergrondtaken hierop, en schakel dan over.

PgBouncer installeren en de applicatie hiernaar laten verwijzen

De onderstaande configuratie is bedoeld voor gebruik op uw eigen server: Ubuntu 24.04, waarbij PostgreSQL al luistert op 127.0.0.1 poort 5432.

sudo apt update
sudo apt install -y pgbouncer
pgbouncer --version

Ubuntu 24.04 levert PgBouncer 1.22.0. De upstream-versie is 1.25.2 per augustus 2026. Controleer welke versie u heeft, aangezien het gedrag van prepared statements hierboven hiervan afhankelijk is.

Maak een rol aan die uitsluitend dient om in te loggen op de PgBouncer-beheerdersconsole en bouw vervolgens het wachtwoordbestand. PgBouncer heeft de SCRAM (salted challenge response authentication mechanism) secrets uit pg_authid nodig, en alleen een superuser kan die tabel uitlezen.

sudo -u postgres psql -c "CREATE ROLE pgb_admin LOGIN PASSWORD 'change-this'"
sudo -u postgres psql -At -c \
  'SELECT format($$"%s" "%s"$$, rolname, rolpassword) FROM pg_authid WHERE rolpassword IS NOT NULL' \
  > /tmp/userlist.txt
sudo install -o postgres -g postgres -m 640 /tmp/userlist.txt /etc/pgbouncer/userlist.txt
rm /tmp/userlist.txt

Het kopiëren van de secrets in plaats van het opnieuw typen van de wachtwoorden zorgt ervoor dat dit werkt. PgBouncer kan een SCRAM-secret alleen hergebruiken om in te loggen op PostgreSQL wanneer de client ook met SCRAM heeft geauthenticeerd, wanneer het secret in het bestand byte-voor-byte overeenkomt met dat in pg_authid (dezelfde salt en iteratie-aantal, niet enkel hetzelfde wachtwoord), en wanneer de [databases]-regel geen user= vastzet. Voeg user=appuser toe aan die regel en PgBouncer vereist in plaats daarvan een wachtwoord in platte tekst. Bevestig dat de eigenaar van het bestand overeenkomt met het account waaronder de service draait, met systemctl show pgbouncer -p User. Het roteren van een wachtwoord in PostgreSQL betekent dat dit bestand opnieuw moet worden gegenereerd, anders resulteert de volgende verbinding in password authentication failed.

Schrijf nu /etc/pgbouncer/pgbouncer.ini.

[databases]
appdb = host=127.0.0.1 port=5432 dbname=appdb

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
admin_users = pgb_admin
pool_mode = transaction
max_client_conn = 500
default_pool_size = 20
min_pool_size = 5
max_db_connections = 80
max_prepared_statements = 200
ignore_startup_parameters = extra_float_digits

listen_addr = 127.0.0.1 houdt de pooler weg van het openbare internet; dit is van belang omdat een pooler die van buitenaf bereikbaar is, een authenticatie-eindpunt vormt dat u niet had willen publiceren. max_client_conn is het aantal applicatieverbindingen dat PgBouncer accepteert; dit is goedkoop en kan daarom hoog zijn. default_pool_size is het aantal echte backends dat één database- en gebruikerspaar mag aanhouden, en dit is het kostbare getal. max_db_connections begrenst de gehele database op 80, waardoor er onder max_connections ruimte overblijft voor psql, back-ups en monitoring. ignore_startup_parameters = extra_float_digits voorkomt dat PgBouncer drivers afwijst, waaronder de JDBC-driver, die die parameter meesturen tijdens het verbinden.

sudo systemctl restart pgbouncer
sudo systemctl status pgbouncer --no-pager
sudo journalctl -u pgbouncer -n 20 --no-pager

Een succesvolle start logt een regel die meldt dat PgBouncer luistert op 127.0.0.1:6432. Een start die faalt op het wachtwoordbestand logt het pad dat niet kon worden gelezen; dit is bijna altijd een probleem met de rechten of het eigenaarschap, en zelden een syntaxfout. Wijzig daarna de connection string van de applicatie van poort 5432 naar poort 6432 en herstart de applicatie. Er hoeft verder niets in de applicatie te worden gewijzigd.

Controleren of de pool naar behoren functioneert

PgBouncer beschikt over een beheerconsole die bereikbaar is via een virtuele database genaamd pgbouncer.

psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer
SHOW POOLS;
SHOW STATS;
SHOW CLIENTS;

SHOW POOLS is de belangrijkste kolom om in de gaten te houden. cl_active staat voor clients die momenteel verbonden zijn met een serververbinding, cl_waiting voor clients die in de wachtrij staan, sv_active en sv_idle zijn de daadwerkelijke backends die in gebruik zijn of beschikbaar zijn, en maxwait geeft aan hoe lang de client vooraan in de wachtrij al wacht, in seconden. Een gezonde status onder normale belasting betekent dat cl_waiting op 0 staat en maxwait op 0. Een maxwait die oploopt tot meer dan een seconde of twee betekent dat de pool te klein is, of dat de queries te traag zijn; beide situaties vereisen verschillende oplossingen.

Controleer welke van de twee het geval is voordat u default_pool_size verhoogt.

SELECT state, count(*) FROM pg_stat_activity
  WHERE backend_type = 'client backend' GROUP BY state;

Als de meeste backends in idle in transaction staan, is de grootte van de pool niet het probleem. De applicatie opent een transactie en voert vervolgens iets trags uit binnen die transactie, zoals een HTTP-aanroep, waardoor elke backend bezet blijft zonder dat er een query wordt uitgevoerd. idle_in_transaction_session_timeout zal deze verbindingen verbreken, maar de werkelijke oplossing ligt in de applicatiecode. Als de backends daarentegen allemaal active zijn, is de pool daadwerkelijk verzadigd en verdienen de queries EXPLAIN (ANALYZE, BUFFERS) voordat u meer verbindingen toestaat.

Wat betreft de dimensionering is het meest geciteerde uitgangspunt de HikariCP-heuristiek van ongeveer twee keer het aantal cores plus één; op een VPS met 2 cores is dat 5. Beschouw dit als een gepubliceerd startgetal, stel default_pool_size hier in de buurt in en pas dit aan op basis van maxwait. Kleine pools voelen vaak onjuist aan, maar presteren in de praktijk meestal beter, omdat een backend in de wachtrij geen resources verbruikt, terwijl een actieve backend CPU, geheugen en lock-contention kost.

Kiezen tussen PgBouncer, PgDog en Pgpool-II

PgBouncer is de oplossing voor de standaard situatie: één PostgreSQL-server, één VPS en een applicatie die meer verbindingen opent dan de server aankan. Het voert één taak uit, de configuratie bestaat uit één enkel ini-bestand en het is beschikbaar in de pakketbronnen van Debian en Ubuntu. Het verwerkt verbindingen in een enkele thread, wat ruim voldoende is voor de werklast van een VPS en pas op veel grotere machines een beperking vormt.

PgDog is het overwegen waard wanneer de routeringsbeslissing in dezelfde netwerkhop moet plaatsvinden als de pooling. Het profileert zichzelf als een proxy voor het schalen van PostgreSQL, is geschreven in Rust en biedt transactie- en sessiepooling, evenals read/write-splitting door queries te parsen. Daarnaast ondersteunt het sharding met multi-shard routering en two-phase commit. Kies hiervoor wanneer u een primaire server en een of meer replica's heeft en u leesacties naar replica's wilt sturen zonder de applicatie aan te passen. Twee waarschuwingen: het is gelicentieerd onder AGPLv3, dus bespreek de clausule voor netwerkgebruik met de verantwoordelijke afdeling binnen uw organisatie voordat u het in productie neemt; het standpunt van het project zelf is dat intern gebruik en privémutaties geen broncodeverplichting creëren. Het is bovendien een jong project met wekelijkse releases en 0.x-versienummers, dus zet de versie vast op een release-tag in plaats van de main te volgen.

git clone https://github.com/pgdogdev/pgdog
cd pgdog
cargo build --release
./target/release/pgdog --config pgdog.toml --users users.toml

Voor het bouwen vanuit de broncode is een actuele stabiele Rust-toolchain, CMake en een C/C++-compiler vereist. Er zijn ook vooraf gebouwde Linux-binaries en Debian-pakketten beschikbaar op de releases-pagina, evenals een container-image op ghcr.io/pgdogdev/pgdog. De configuratie is verdeeld over twee bestanden. Het eerste bevat de algemene instellingen en één vermelding per database, hier geschreven als een TOML-array van inline tabellen zodat de twee vormen eenvoudig te onderscheiden blijven.

databases = [
  { name = "appdb", host = "127.0.0.1" },
]

[general]
port = 6432
default_pool_size = 10

Het tweede bestand bevat één vermelding per gebruiker, in dezelfde array-vorm.

users = [
  { name = "appuser", database = "appdb", password = "change-this" },
]

PgDog luistert standaard op poort 6432, dezelfde poort als PgBouncer, waardoor ze niet beide op dezelfde host de standaardpoort kunnen gebruiken.

Pgpool-II, versie 4.7.2 per juni 2026, biedt pooling en load balancing, inclusief een watchdog voor automatische failover. De extra functies introduceren extra faalmodi; het is essentieel om het pooling-model te begrijpen voordat u hiervoor kiest. Pgpool-II start vooraf num_init_children child-processen, en elk child cacht maximaal max_pool serververbindingen, waardoor het maximum aantal backends gelijk is aan num_init_children vermenigvuldigd met max_pool. Elk child bedient slechts één client tegelijk, dus het aantal clients dat u kunt accepteren is gelijk aan num_init_children en staat vast bij het opstarten; een inactieve client bezet nog steeds een child. Als u num_init_children instelt op 100 en max_pool op 4, heeft u 400 backends geautoriseerd, wat precies het probleem is dat u met een pooler probeerde op te lossen. Kies voor Pgpool-II wanneer u behoefte heeft aan failover en query-routering, en voer die vermenigvuldiging zorgvuldig uit. Als u enkel het aantal backends wilt verminderen, is dit meer infrastructuur dan nodig is.

De vraag over managed proxies en het zelfgehoste equivalent

Managed platforms verkopen dit als een apart product. AWS plaatst RDS Proxy voor RDS, en Supabase plaatst zijn eigen pooler, Supavisor, voor Supabase Postgres. Beide doen het werk dat hier wordt beschreven: ze houden clientverbindingen goedkoop vast en verdelen een kleiner aantal echte backends. Supavisor is open source en kan zelf worden gehost, dus de keuze is niet tussen propriëtair en gratis.

Het zelfgehoste equivalent van een managed proxy is geen ander concept. Het is hetzelfde idee, maar dan met het configuratiebestand in uw eigen beheer: PgBouncer in transaction mode, op dezelfde VPS als de database, luisterend op 127.0.0.1. Er zijn twee wezenlijke verschillen. Een managed proxy bevindt zich op een netwerkhop afstand, waardoor deze latentie toevoegt en clientverbindingen vasthoudt terwijl de database eronder herstart. PgBouncer op de databasehost voegt een loopback-hop toe, wat nagenoeg gratis is, maar deze stopt wanneer de host uitvalt. Als u het gedrag wilt waarbij verbindingen een herstart overleven, heeft u ook failover-mechanismen nodig; dat is waar de complexiteit van de watchdog van Pgpool-II of de health checks van PgDog zich begint terug te verdienen.

Er hoort nog één optie op de lijst te staan. Als het aantal verbindingen de voornaamste reden is waarom uw implementatie ingewikkeld wordt, heeft een embedded database geen verbindingsmodel om te poolen, omdat het een bibliotheek binnen uw proces is in plaats van een server op een poort. Voor een enkele applicatieserver met een bescheiden schrijfvolume, verwijdert het draaien van SQLite in productie op een VPS dit hele probleem in plaats van het te beheren. Wanneer u wel een echte server nodig heeft, bepaal dan eerst de grootte van de pool voordat u de grootte van de machine bepaalt.

FAQ

Heb ik nog steeds PgBouncer nodig als mijn applicatie al een connection pool heeft?

Meestal wel, omdat een applicatie-pool per proces werkt en de andere processen niet kan zien. Vier Gunicorn-workers die elk een pool van 20 verzoeken 80 backends aanhouden, en het toevoegen van twee achtergrond-workers maakt dit 120. PgBouncer is de enige component die het totaal ziet en dit kan begrenzen. De juiste opzet is beide: een kleine pool binnen elke worker zodat verzoeken geen TCP-connectie hoeven op te zetten, en PgBouncer in transaction mode die de werkelijke backends daarachter beperkt.

Wat gaat er precies kapot als ik PgBouncer overschakel naar transaction mode?

Alles wat de status in één backend behoudt tussen transacties door. SET en RESET op sessieniveau, LISTEN, WITH HOLD cursors, de SQL-statements PREPARE en DEALLOCATE, advisory locks op sessieniveau, tijdelijke tabellen die een commit moeten overleven, en LOAD. NOTIFY blijft werken, waardoor een defecte LISTEN lijkt op een afleveringsfout in plaats van een pooling-probleem. Stel in Django DISABLE_SERVER_SIDE_CURSORS in op True. Stel bij psycopg 3 ofwel prepare_threshold in op None, of draai PgBouncer 1.22 of nieuwer met max_prepared_statements boven 0. Vervang pg_advisory_lock() door pg_advisory_xact_lock().

Hoe groot moet default_pool_size zijn op een VPS met 2 cores?

Kleiner dan intuïtief lijkt. De veelgeciteerde HikariCP-heuristiek is ongeveer twee keer het aantal cores plus één, dus grofweg 5 op twee cores, en dat is een startpunt in plaats van een definitief antwoord. Stel het in, lees daarna maxwait en cl_waiting in SHOW POOLS onder werkelijke belasting. Nul bij beide betekent dat de pool groot genoeg is. Een stijgende maxwait betekent dat clients in de wachtrij staan; controleer voordat u het aantal verhoogt eerst pg_stat_activity: backends die vastzitten in idle in transaction zijn een applicatiefout die meer verbindingen alleen maar zal maskeren.

PgBouncer of PgDog?

PgBouncer voor één PostgreSQL-server op één VPS, wat voor de meeste implementaties geldt. Het is verpakt in Ubuntu, het gedrag is goed gedocumenteerd en de volledige configuratie bestaat uit één ini-bestand. PgDog is geschikt wanneer read/write-splitting over replica's of sharding in dezelfde hop als de pooling moet plaatsvinden, zodat de applicatie de topologie niet hoeft te kennen. Voordat u kiest voor PgDog, moet u de AGPLv3-kwestie afstemmen met de licentiehouder binnen uw organisatie en een specifieke release vastzetten, aangezien het project nog op 0.x-versienummers zit en wekelijks nieuwe releases uitbrengt.

#postgres#pgbouncer#pgdog#connections#performance