Zum Inhalt

PostgreSQL Connection Pooling: Architektur, Sizing & PgBouncer im Produktivbetrieb

Erstellt: 2026-11-08  ·  Zuletzt aktualisiert: 2026-11-08  ·  Nejat Hakan

Management Summary: PostgreSQL nutzt eine prozessbasierte Concurrency-Architektur (Process-per-Connection): Jeder verbundene Client erzeugt einen eigenständigen Backend-Prozess, der 5–15 MB RAM, private Speicherbereiche und CPU-Scheduler-Slices bindet. Ein schlichtes Hochsetzen von max_connections = 2000 in postgresql.conf führt in Produktionsumgebungen unweigerlich zu drastischem CPU-Cache-Thrashing, explodierenden Abfragelatenzen und OOM-Kills (Out-of-Memory). Dieser Praxisleitfaden demonstriert, wie PgBouncer als performanter Connection-Proxy unter Debian Linux eingerichtet wird, wie Connection-Pools nach der Warteschlangentheorie dimensioniert werden und wie /etc/pgbouncer/pgbouncer.ini für hochperformante Microservice-Architekturen konfiguriert wird.

  • Process-per-Connection Overhead


    Den Flaschenhals verstehen: Warum hunderte untätige Backend-Prozesse den Linux-Scheduler überlasten und physischen RAM vergeuden.

  • Vergleich der Pooling-Modi


    Session vs. Transaction vs. Statement: Den passenden Modus anhand von ORM-Semantiken und Transaktionsgrenzen fundiert auswählen.

  • Produktionskonfiguration


    Härtung von /etc/pgbouncer/pgbouncer.ini unter Debian: Pool-Sizing-Formeln, SCRAM-Authentifizierung, Timeouts und Systemd-Service.

  • Admin-Konsole & Benchmarking


    Pool-Gesundheit live über administrative Pseudo-Tabellen (SHOW POOLS, SHOW CLIENTS) überwachen und Durchsatzgewinne mit pgbench verifizieren.


1. Das Process-per-Connection-Dilemma & Pool-Sizing

Im Gegensatz zu Thread-basierten Datenbanksystemen (wie MySQL oder MS SQL) forkt PostgreSQL für jeden verbundenen Client einen vollständig autonomen Betriebssystem-Prozess. Wenn hunderte Microservices parallel Verbindungen öffnen:

%%{init: {'flowchart': {'wrappingWidth': 350}}}%%
flowchart TB
    CLI["1.000+ App-Verbindungen<br/>(Inaktive Microservices)"]
    PGB["PgBouncer Proxy-Dienst<br/>(libevent Event-Loop)"]
    PG["PostgreSQL Server-Engine<br/>(Fester Pool: 30 Backends)"]
    RAM["Physischer RAM<br/>Geschützt vor OOM"]

    CLI -->|"Schnelle Handshakes"| PGB
    PGB -->|"Multiplext Queries"| PG
    PG -->|"CPU-Cache-Treffer"| RAM

Die Mathematik des Verbindungs-Overheads

  1. Speicherfraß: 1.000 inaktive Backend-Prozesse belegen jeweils 5–15 MB privaten Arbeitsspeicher (RSS) – das bindet 10–15 GB RAM, ohne eine einzige Abfrage zu berechnen.
  2. Context-Switching: Wachen hunderte Prozesse gleichzeitig auf, um kurze Queries auszuführen, verbringt der Linux-CPU-Scheduler erhebliche Zyklen mit Kontextwechseln. Die CPU-L1/L2/L3-Caches werden entwertet (Cache Thrashing).
  3. Die Dimensionierungsformel: Der maximale Durchsatz einer relationalen Datenbank wird erreicht, wenn die Anzahl aktiver Server-Backends exakt auf die Hardwarekapazitäten abgestimmt ist:

Max. aktive Verbindungen ≈ (CPU-Kerne × 2) + Effektive NVMe-Kanäle / Spindeln

Auf einem Bare-Metal-Server mit 16 CPU-Kernen und NVMe-Speicher liefert ein interner PostgreSQL-Pool von 32 bis 40 aktiven Backend-Prozessen den maximalen Transaktionsdurchsatz. PgBouncer bedient davor problemlos 5.000+ Client-Verbindungen bei minimalem Speicheraufwand.


2. Vergleich der PgBouncer-Pooling-Modi

Merkmal / Verhalten Session Pooling (session) Transaction Pooling (transaction) Statement Pooling (statement)
Verbindungs-Lebenszyklus Wird beim Client-Connect zugewiesen; Freigabe erst beim Disconnect. Zuweisung bei BEGIN (oder erster Query); Freigabe sofort bei COMMIT/ROLLBACK. Zuweisung pro einzelnem SQL-Statement; sofortige Rückgabe.
Multiplexing-Effizienz Gering (Server-Verbindung blockiert während Client-Idle-Zeiten). Extrem hoch (100 Server-Verbindungen bedienen 10.000 Clients). Maximal, verhindert aber Multi-Statement-Transaktionen.
Prepared Statements Uneingeschränkt und nativ unterstützt. In modernem PgBouncer (ab v1.21+) via Protokoll-Tracking unterstützt. Strikt verboten.
Session-Variablen (SET) Bleiben über die gesamte Client-Session hinweg erhalten. Werden zwischen Transaktionen verworfen (erfordert SET LOCAL). Strikt verboten.
Empfohlener Einsatzzweck Monolithische Legacy-Anwendungen mit Session-Abhängigkeiten. Moderne Web-APIs, Microservices, ORMs (Prisma, Django, Hibernate). Reine Key-Value-Lese-Caches ohne Transaktionslogik.

3. Produktions-Setup von PgBouncer unter Debian

3.1. Paket-Installation

Installieren Sie PgBouncer aus den offiziellen Debian-Repositories:

sudo apt-get update
sudo apt-get install -y pgbouncer

3.2. Konfiguration: /etc/pgbouncer/pgbouncer.ini

; Datei: /etc/pgbouncer/pgbouncer.ini
[databases]
; Mappt externe Verbindungsanfragen auf die lokale PostgreSQL-Instanz
enterprise_prod = host=127.0.0.1 port=5432 dbname=enterprise_prod auth_user=postgres
* = host=127.0.0.1 port=5432

[pgbouncer]
; Netzwerk-Konfiguration
listen_addr = 0.0.0.0
listen_port = 6432
unix_socket_dir = /var/run/postgresql

; Authentifizierung
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
auth_query = SELECT usename, passwd FROM pg_shadow WHERE usename=$1

; Dimensionierung des Connection-Pools
pool_mode = transaction
max_client_conn = 5000
default_pool_size = 30
min_pool_size = 10
reserve_pool_size = 5
reserve_pool_timeout = 5.0
max_db_connections = 60

; Timeouts und Verbindungskontrolle
server_idle_timeout = 600
server_connect_timeout = 15
client_idle_timeout = 0
query_timeout = 120

; Logging und Administration
admin_users = postgres, admin
stats_users = monitoring, nagios, prometheus
logfile = /var/log/postgresql/pgbouncer.log
pidfile = /var/run/postgresql/pgbouncer.pid

3.3. Authentifizierungs-Konfiguration: /etc/pgbouncer/userlist.txt

PgBouncer nutzt eine Datei mit Benutzerkonten und SCRAM-Hashes:

; Datei: /etc/pgbouncer/userlist.txt
"postgres" "SCRAM-SHA-256$4096:5a9...=="
"app_runtime_user" "SCRAM-SHA-256$4096:7b2...=="

Berechtigungen absichern und Dienst aktivieren:

sudo chown postgres:postgres /etc/pgbouncer/userlist.txt /etc/pgbouncer/pgbouncer.ini
sudo chmod 600 /etc/pgbouncer/userlist.txt

sudo systemctl enable --now pgbouncer
sudo systemctl status pgbouncer

4. Live-Administration & Stresstests mit pgbench

4.1. Verbindung zur virtuellen Admin-Konsole

PgBouncer stellt eine interne Pseudo-Datenbank namens pgbouncer für Echtzeitdiagnosen bereit:

psql -h 127.0.0.1 -p 6432 -U postgres -d pgbouncer

Wichtige Diagnosebefehle:

-- Zustand der Connection-Pools prüfen
SHOW POOLS;

-- Ausgabe:
--     database     |   user   | cl_active | cl_waiting | sv_active | sv_idle | sv_used | sv_tested | maxwait 
-- -----------------+----------+-----------+------------+-----------+---------+---------+-----------+---------
--  enterprise_prod | app_user |       250 |          0 |        28 |       2 |       0 |         0 |       0

-- Aktive Client-Verbindungen inspizieren
SHOW CLIENTS;

-- Aktive Server-Backend-Verbindungen inspizieren
SHOW SERVERS;

-- Aggregierte Durchsatz-Statistiken abrufen
SHOW STATS;

4.2. Benchmarking: Direktverbindung vs. PgBouncer (pgbench)

Stresstest mit 1.000 parallelen Clients:

Direktverbindung (Führt zu Überlastung oder too many connections)

pgbench -h 127.0.0.1 -p 5432 -U app_runtime_user -c 1000 -j 10 -t 50 enterprise_prod
# Ergebnis: ERROR: FATAL: remaining connection slots are reserved for non-replication superuser connections

Verbindung über PgBouncer (Stabiler Durchsatz, minimale Wartelatenz)

pgbench -h 127.0.0.1 -p 6432 -U app_runtime_user -c 1000 -j 10 -t 50 enterprise_prod
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 50
query mode: simple
number of clients: 1000
number of threads: 10
maximum number of tries: 1
number of transactions per client: 50
number of transactions actually processed: 50000/50000
latency average = 14.281 ms
initial connection time = 182.102 ms
tps = 14210.421051 (without initial connection time)

Über PgBouncer werden 1.000 parallele Client-Verbindungen sauber über lediglich 30 Server-Prozesse gemultiplext – ohne Socket-Abbrüche und mit stabiler Minimal-Latenz.