Naar de inhoud
maarten.
Alle stories

Database connection pooling: verbindingen beheren

Maarten Soetens 12 min lezen

Een connection pool hergebruikt databaseverbindingen, zodat applicaties niet voor ieder verzoek een nieuwe verbinding hoeven op te bouwen. Lees hoe pools werken, hoe je hun omvang afstemt op applicatie en database en welke signalen wijzen op verbindingsproblemen.

Hoe een database connection pool verbindingen hergebruikt

Een databaseverbinding opzetten kost tijd en servercapaciteit. De client moet onder meer een netwerkverbinding openen, authenticeren en eventueel TLS en sessie-instellingen afhandelen. Een connection pool voert dit werk vooraf uit en houdt een verzameling actieve verbindingen beschikbaar. Wanneer een applicatie een query wil uitvoeren, leent zij tijdelijk een verbinding uit de pool. Na gebruik gaat die terug naar de pool, in plaats van dat de verbinding direct wordt gesloten.

Dat hergebruik verlaagt de overhead bij veel korte databasebewerkingen. Het maakt ook het aantal gelijktijdige verbindingen beter beheersbaar: de pool kan een bovengrens instellen en verzoeken laten wachten wanneer alle verbindingen bezet zijn. De pool maakt databasewerk echter niet gratis. Een verbinding die ongebruikt openstaat, neemt nog steeds resources in op de database en kan bijvoorbeeld een sessie of geheugenruimte bezetten.

Een pool is meestal onderdeel van de applicatie of de databaseclientbibliotheek. Sommige omgevingen gebruiken daarnaast een aparte proxy die verbindingen tussen clients en database multiplexeert. Die oplossingen hebben verschillende grenzen en instellingen. Wie wil begrijpen hoeveel verbindingen er werkelijk ontstaan, moet daarom onderscheid maken tussen applicatieverzoeken, poolslots, verbindingen naar een proxy en fysieke databasesessies.

Waarom onbeperkte verbindingen de database uitputten

Zonder limiet kan een applicatie voor ieder gelijktijdig verzoek een eigen databaseverbinding openen. Bij een rustige belasting lijkt dat vaak prima te werken. Tijdens een piek neemt de concurrency toe en groeit ook het aantal verbindingen. De database moet voor elke sessie resources beheren; veel databases reserveren bovendien werkgeheugen voor verbindingen of voor bewerkingen die via die verbinding lopen. Zodra de server haar limiet bereikt, kunnen nieuwe clients niet meer inloggen.

De gevolgen beperken zich niet tot verzoeken die databasewerk nodig hebben. Als verbindingen schaars worden, lopen wachtrijen op en kunnen achtergrondtaken, beheerverbindingen of gezondheidscontroles eveneens vertraging oplopen. Een applicatie kan daarop reageren door opnieuw verbinding te proberen. Dat vergroot de druk juist op het moment dat de database al overbelast is. Meerdere applicatie-instanties die tegelijk herstarten, kunnen zo een reconnect-storm veroorzaken.

Een connection pool begrenst het aantal gelijktijdige verbindingen per proces, maar is niet automatisch een globale limiet. Bij twintig instanties met elk een pool van dertig verbindingen kan de database in theorie tot zeshonderd verbindingen ontvangen. Houd daarom rekening met alle services en omgevingen die dezelfde database delen, inclusief workers, migratietools en monitoring. Een pool zonder totaalbeeld verplaatst het probleem van ongecontroleerde groei naar gecontroleerde, maar nog steeds te grote groei.

Poolgrootte afstemmen op applicatie en database

De juiste poolgrootte volgt niet rechtstreeks uit het aantal applicatieverzoeken. Een verzoek kan kort databasewerk doen en daarna veel tijd besteden aan netwerkverkeer of verwerking. In dat geval is het aantal gelijktijdige databasebewerkingen lager dan de totale requestconcurrency. Omgekeerd kunnen trage queries een verbinding lang bezet houden, waardoor een kleine pool snel volloopt. Begin daarom met meten hoeveel tijd verbindingen werkelijk in gebruik zijn en hoeveel gelijktijdige databasebewerkingen de applicatie veroorzaakt.

De bovengrens moet passen binnen het verbindingsbudget van de database. Reserveer capaciteit voor andere services, beheer, failover en tijdelijke pieken. Deel het resterende budget door het maximale aantal applicatie-instanties om een eerste limiet per instantie te bepalen. Neem daarbij autoscaling mee: een poolinstelling die bij vijf instanties past, kan bij dertig instanties alsnog de databaseserver overbelasten. Een globale proxylimiet kan extra bescherming bieden, maar vervangt geen inzicht in de lokale wachtrijen.

Een grotere pool verhoogt de mogelijke paralleliteit, maar kan de database ook meer gelijktijdig werk opleggen. Wanneer CPU of opslag al verzadigd is, leidt extra concurrency vaak tot langere querytijden en meer contention, niet tot hogere doorvoer. Een kleinere pool kan de database beschermen, maar veroorzaakt wachttijd in de applicatie als de belasting structureel hoger ligt dan de verwerkingscapaciteit. Test daarom onder representatieve belasting en beoordeel zowel doorvoer als wachttijd en databasebelasting.

Time-outs en wachtrijen in de connection pool instellen

Wanneer alle poolverbindingen bezet zijn, moet een nieuw verzoek wachten of falen. De acquire-time-out bepaalt hoe lang de applicatie op een vrije verbinding wacht. Een onbeperkte wachttijd kan verzoeken opstapelen tot threads, taken of geheugen uitgeput raken. Een zeer korte time-out veroorzaakt juist fouten bij kleine, tijdelijke pieken. Kies de grens in relatie tot de totale requestdeadline: databaseverbinding verkrijgen mag niet de volledige tijd opsouperen die nodig is om de query uit te voeren en het antwoord terug te sturen.

Verschillende time-outs bewaken verschillende fasen. Een connect-time-out begrenst het openen van een nieuwe verbinding; een query-time-out beperkt hoe lang een statement mag draaien; een idle-time-out bepaalt hoe lang een ongebruikte verbinding in de pool blijft. Een maximale levensduur kan verbindingen periodiek vernieuwen, bijvoorbeeld om oude sessies of netwerkpaden te vervangen. Deze instellingen zijn niet uitwisselbaar. Een query-time-out lost bijvoorbeeld geen lange wachtrij op wanneer de applicatie nog geen verbinding heeft gekregen.

Stem time-outs af op retries en foutafhandeling. Als een verzoek na een acquire-time-out direct meerdere keren opnieuw probeert, ontstaat extra druk zonder dat de capaciteit verandert. Retries horen begrensd te zijn en waar mogelijk een toenemende wachttijd met willekeurige spreiding te gebruiken. Controleer ook of de applicatie bij een time-out een lopende query annuleert en de verbinding correct teruggeeft of afvoert. Anders kan een fout aan de clientzijde alsnog een bezette databasesessie achterlaten.

Verbindingen veilig lenen en teruggeven

Een pool werkt alleen goed als applicatiecode iedere geleende verbinding weer vrijgeeft. Gebruik daarom constructies die opruimwerk ook uitvoeren wanneer een query een fout geeft of een exception optreedt. In veel frameworks gebeurt dit met een contextmanager, een finally-blok of een transactie-API die de levensduur expliciet afbakent. Een verbinding die door een foutpad niet wordt teruggegeven, blijft bezet. Na verloop van tijd raakt de pool dan uitgeput, hoewel de database zelf mogelijk nog capaciteit heeft.

Een transactie moet zo kort mogelijk blijven en alleen de bewerkingen omvatten die atomair moeten zijn. Wacht niet binnen een transactie op een externe API, gebruikersinvoer of een lange berekening. De verbinding blijft gedurende die tijd gereserveerd en locks kunnen langer worden vastgehouden. Dat verlaagt de effectieve poolcapaciteit en kan andere transacties blokkeren. Zet ook geen gedeelde verbinding in voor gelijktijdige taken als de driver of het framework dat niet expliciet ondersteunt; cursors en transactiestatus kunnen elkaar dan beïnvloeden.

Een verbinding teruggeven betekent niet altijd dat de sessie exact in de oorspronkelijke toestand verkeert. Instellingen voor schema, tijdzone, isolatieniveau of tijdelijke tabellen kunnen blijven bestaan. Gebruik waar mogelijk transacties en frameworkfunctionaliteit die sessiestatus resetten, of maak zulke wijzigingen expliciet ongedaan. Als een verbinding na een protocolfout of netwerkonderbreking niet betrouwbaar is, moet de pool haar markeren als ongeldig en sluiten. Een defecte verbinding terugplaatsen zorgt ervoor dat volgende verzoeken dezelfde fout opnieuw tegenkomen.

Poolgedrag bij meerdere processen en applicatie-instanties

Veel applicaties gebruiken meerdere processen of containers, en iedere instantie heeft doorgaans een eigen pool. De ingestelde poolgrootte geldt dan per proces, niet voor de hele applicatie. Een deployment met meerdere workers kan daardoor veel meer databasesessies openen dan verwacht. Neem bij capaciteitsschattingen het maximale aantal instanties, processen per instantie en poollimiet per proces mee. Tel daarnaast afzonderlijke pools op voor read-replica's, achtergrondworkers en andere services die dezelfde database gebruiken.

Ook het moment waarop pools worden aangemaakt is van belang. Een pool die al verbindingen opent voordat een proces zich splitst, kan onder bepaalde processmodellen verbindingen delen op een manier die de databaseclient niet veilig ondersteunt. Initialiseer pools daarom op het juiste moment voor de gekozen runtime en volg de aanbevelingen van de driver. Bij autoscaling kan een plotselinge reeks nieuwe instanties bovendien tegelijk verbindingen opbouwen. Een maximum per instantie voorkomt dan niet noodzakelijk een korte piek op de database.

Een externe connection proxy kan applicatieverbindingen beheren en, afhankelijk van de modus, een kleiner aantal databasesessies delen tussen clients. Dat kan nuttig zijn bij veel kortlevende processen of serverless functies. Multiplexing werkt niet voor iedere sessie-eigenschap: langdurige transacties, sessievariabelen of tijdelijke objecten kunnen een backendverbinding langer vastzetten. Controleer daarom welke databasefuncties de proxy ondersteunt en meet zowel de clientwachtrij als het aantal serververbindingen. Anders kan een probleem alleen van de database naar de proxywachtrij verschuiven.

Verbindingsproblemen herkennen met metrics en logs

Een volle pool herken je niet alleen aan databasefouten. Meet hoeveel verbindingen actief zijn, hoeveel idle beschikbaar zijn en hoeveel verzoeken op een verbinding wachten. Leg ook de tijd vast tussen het aanvragen en verkrijgen van een verbinding. Een stijgende acquire-latency met een pool die voortdurend op haar limiet zit, wijst op onvoldoende capaciteit, lang bezette verbindingen of een database die de queries niet snel genoeg verwerkt. Het onderscheid vraagt om aanvullende metingen van queryduur, transactieduur en databasebelasting.

Maak onderscheid tussen pooluitputting en verbindingsfouten op netwerk- of databasevlak. Pooluitputting geeft vaak wachttijd voordat een verbinding beschikbaar komt; een connectiefout ontstaat tijdens het openen of gebruiken van de verbinding. Logs kunnen de foutklasse, time-outfase en oorzaak vastleggen, maar vermijd credentials, volledige queryparameters of persoonsgegevens. Gebruik queryfingerprints of geanonimiseerde statementnamen om terugkerende trage patronen te vinden zonder gevoelige waarden te loggen.

Bekijk metrics over tijd en per instantie. Een gemiddelde kan korte pieken verbergen, terwijl één worker met gelekte verbindingen achterblijft bij de rest. Nuttige signalen zijn het percentage tijd dat de pool verzadigd is, het aantal time-outs bij het verkrijgen van verbindingen, reconnects, idle verbindingen en het aantal databaseconnecties. Vergelijk die met requestvolume, foutpercentage en querylatency. Wanneer alle instanties tegelijk vertragen, ligt een gedeelde databaseoorzaak meer voor de hand; een probleem op één instantie kan wijzen op een lokaal lek of afwijkend werkpatroon.

Poolinstellingen testen bij piekbelasting en storingen

Een poolinstelling beoordelen op basis van een ontwikkelomgeving geeft weinig informatie over productiegedrag. Test met een belasting die de concurrency, querymix en transactieduur van de echte applicatie benadert. Verhoog de belasting gecontroleerd en meet doorvoer, requestlatency, wachttijd op verbindingen, queryduur en databaseverzadiging. Een pool die bij lage belasting snel reageert, kan onder piekbelasting een lange wachtrij vormen. Een grotere pool kan die wachtrij verkorten maar tegelijkertijd CPU, geheugen of opslag op de database overbelasten.

Test ook scenario's waarin de database tijdelijk trager reageert, verbindingen worden verbroken of een failover plaatsvindt. Controleer hoe de pool defecte sessies detecteert, hoe snel nieuwe verbindingen worden geprobeerd en of retries zich verspreiden. Een agressieve herstelstrategie kan na een storing een golf van loginpogingen veroorzaken. Een te terughoudende strategie laat juist lang verbindingen circuleren die niet meer bruikbaar zijn. Let tijdens de test op het verschil tussen bestaande sessies herstellen en nieuwe sessies opbouwen.

Wijzig bij tuning bij voorkeur één relevante instelling tegelijk en vergelijk met een vastgelegde uitgangssituatie. Houd rekening met cache-effecten, variërende queryplannen en de verdeling van korte en lange transacties. Controleer daarnaast de som van de poollimieten bij het maximale aantal instanties; een test met één worker toont die grens niet. Leg instellingen en meetresultaten per omgeving vast, zodat verschillen tussen applicatieversies, databaseconfiguraties en autoscalingregels verklaarbaar blijven.

Veelgestelde vragen

Wat moet ik doen met databaseverbindingen tijdens een failover?

Tijdens een failover moet de pool defecte verbindingen afvoeren en nieuwe verbindingen met begrensde vertraging opnieuw opbouwen. Bestaande sessies kunnen naar de oude database wijzen of zijn verbroken, ook als ze nog in de pool staan. Zorg daarom dat fouten bij het lenen of gebruiken van een verbinding tot vervanging leiden.

Laat applicatieverzoeken niet onbeperkt opnieuw proberen. Gebruik een beperkt aantal retries met oplopende wachttijden en willekeurige spreiding. Herhaal alleen bewerkingen waarvan je weet dat dit veilig is: een onderbroken transactie kan al gedeeltelijk zijn uitgevoerd. Test ook hoe DNS-wijzigingen en eventuele promotie van een replica doorwerken in de gebruikte driver en infrastructuur.

Hoe gebruik ik een connection pool in een serverless functie?

Gebruik in een serverless functie bij voorkeur een herbruikbare pool per warme uitvoeringsomgeving, maar houd die pool klein en begrens het totale aantal verbindingen. Serverless platforms kunnen veel omgevingen tegelijk starten; iedere omgeving kan haar eigen pool maken. Een bescheiden limiet per omgeving kan daardoor alsnog een grote gezamenlijke piek veroorzaken.

Controleer hoe lang de omgeving blijft bestaan en of verbindingen na inactiviteit door de database of het netwerk worden gesloten. Een databaseproxy kan helpen om veel clientverbindingen te bundelen, mits de gebruikte transacties en sessiefuncties daarmee werken. Test pieken in gelijktijdige functie-uitvoeringen en koude starts, niet alleen één functieaanroep in een ontwikkelomgeving.

Moet een connection pool altijd een minimumaantal verbindingen openhouden?

Nee, een connection pool hoeft niet altijd verbindingen open te houden; de juiste minimumgrootte hangt af van de belasting en de kosten van het opnieuw opzetten van verbindingen. Een hoger minimum kan de eerste verzoeken na een rustige periode sneller laten starten, maar ongebruikte verbindingen blijven databasecapaciteit innemen.

Bij onregelmatige of lage belasting kan een minimum van nul of een klein aantal verbindingen passend zijn, eventueel gecombineerd met een idle-time-out. Bij een stabiele, drukke workload kan een hoger minimum de opstartvertraging beperken. Meet de verbindingsopbouwtijd en het gebruik door de dag heen en neem ook mee hoeveel applicatie-instanties tegelijk kunnen draaien.

Hoe bereken ik een eerste poolgrootte met queryduur en verzoekvolume?

Een eerste schatting van de benodigde gelijktijdige verbindingen krijg je door het aantal databasebewerkingen per seconde te vermenigvuldigen met de gemiddelde tijd dat elke bewerking een verbinding vasthoudt. Als een applicatie bijvoorbeeld 40 databasebewerkingen per seconde uitvoert en een verbinding gemiddeld 0,1 seconde bezet is, is de gemiddelde gelijktijdigheid ongeveer 4 verbindingen.

Gebruik dit als startpunt, niet als definitieve instelling: pieken, transacties en variatie in queryduur vragen om aanvullende ruimte. Meet de werkelijke wachttijd en bezettingsgraad onder representatieve belasting. Controleer vervolgens of de som van alle pools, inclusief workers en andere instanties, binnen het verbindingsbudget van de database blijft.

Hoe sluit ik een connection pool veilig af tijdens een deployment?

Sluit een connection pool veilig af door eerst te stoppen met het aannemen van nieuw werk, lopende verzoeken de kans te geven hun databasewerk af te ronden en daarna de pool te sluiten. Zo voorkom je dat actieve verzoeken verbindingen verliezen doordat een deployment de applicatie te vroeg beëindigt.

Stel hiervoor een maximale wachttijd in die past binnen de grace period van je procesmanager of containerplatform. Als verzoeken na die termijn nog actief zijn, moet de applicatie gecontroleerd afbreken in plaats van onbeperkt blijven hangen. Test ook wat er gebeurt bij een geforceerde herstart en zorg dat poolinitialisatie en poolafsluiting aansluiten op het procesmodel van je runtime.

Portret van Maarten

Maarten

Freelance developer in Nijmegen

Even kennismaken?

Vertel kort wat er speelt. Dan hoor je wat er kan, wat ik anders zou doen en waar AI bij jou wél en niet iets toevoegt. Vrijblijvend.

[email protected]
Het kantoor in Nijmegen
© 2026 maarten.online Sitemap Privacy Algemene voorwaarden
Het kantoor in Nijmegen

Maarten.

Freelance developer in Nijmegen. Liever direct contact? Dat kan ook.

Kennismaken

Laat je gegevens achter, dan kijken we of het klikt. Vrijblijvend en zonder verkooppraat.

Maarten

Stuur een bericht via WhatsApp

Hoi! Waar kan ik je mee helpen?

nu