Eine hängende Query, eine vergessene Migration, ein Tool, das die Connection nicht freigibt — manchmal müssen Sessions zwangsweise beendet werden. PostgreSQL bietet dafür zwei Stufen: pg_cancel_backend als „bitte hör auf” und pg_terminate_backend als „du gehst jetzt”. Mit pg_stat_activity siehst du, wer überhaupt verbunden ist.

Wer ist gerade verbunden? — pg_stat_activity

Die zentrale Sicht ist pg_stat_activity. Sie zeigt jeden Backend-Prozess mit User, Datenbank, Client-IP, aktueller Query und State:

SQL Aktive Sessions
SELECT pid, usename, datname, client_addr, state,
       state_change, query
FROM pg_stat_activity
WHERE state IS NOT NULL
ORDER BY state_change;

Beispiel-Ausgabe:

SQL
  pid  | usename  | datname | client_addr  |        state        |     state_change      |       query
-------+----------+---------+--------------+---------------------+-----------------------+-------------------
 12345 | app_user | myapp   | 10.0.0.5     | active              | 2026-05-06 11:32:17+00| SELECT ...
 12346 | bob      | myapp   | 10.0.0.7     | idle in transaction | 2026-05-06 09:15:08+00| BEGIN
 12347 | reporting| myapp   | 10.0.0.8     | idle                | 2026-05-06 11:30:00+00|

Die wichtigsten state-Werte:

StateBedeutung
activeSession führt gerade eine Query aus
idleSession ist verbunden, aber gerade untätig
idle in transactionSession hat eine offene Transaktion und tut nichts — gefährlich!
idle in transaction (aborted)Transaktion ist abgebrochen, aber ROLLBACK/COMMIT fehlt

idle in transaction ist der häufigste Auslöser für „mein VACUUM macht keinen Fortschritt mehr”: eine offene Transaktion verhindert, dass tote Tupel weggeräumt werden.

Lange laufende Queries finden

SQL Queries, die laenger als 5min laufen
SELECT pid, usename, datname,
       now() - query_start AS duration,
       state, query
FROM pg_stat_activity
WHERE state = 'active'
  AND now() - query_start > interval '5 minutes'
ORDER BY duration DESC;

Praktisch als Live-Dashboard mit \watch 5 in psql:

SQL
myapp=# SELECT pid, now()-query_start AS dur, query
        FROM pg_stat_activity WHERE state='active';
myapp=# \watch 5

Sanftes Abbrechen — pg_cancel_backend

pg_cancel_backend(pid) sendet das Signal „bitte aktuellen Befehl abbrechen”. Die Session selbst bleibt verbunden.

SQL
SELECT pg_cancel_backend(12345);

Anwendungsfall: eine Reporting-Query läuft seit Stunden und blockiert Ressourcen — du willst sie stoppen, der User kann aber an seiner psql-Session bleiben und eine andere Query absetzen.

Wichtig: pg_cancel_backend wirkt nur auf aktive Queries. Eine idle in transaction-Session ignoriert das Signal — dafür brauchst du pg_terminate_backend.

Endgültiges Beenden — pg_terminate_backend

pg_terminate_backend(pid) zieht der Session den Stecker. Die Connection wird geschlossen, offene Transaktionen werden gerollt, der Backend-Prozess endet.

SQL
SELECT pg_terminate_backend(12346);

Wer darf das? Default: nur Superuser oder Mitglieder von pg_signal_backend. Eine Rolle kann immer ihre eigenen Sessions beenden — andere Sessions nur mit höherem Privileg.

SQL Oncall-Rolle ohne Superuser
GRANT pg_signal_backend TO oncall;

Alle Sessions eines Users beenden

Praktisch z. B. nach dem Ausscheiden eines Mitarbeiters:

SQL bob beendet alles, was bob laeuft
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE usename = 'bob'
  AND pid <> pg_backend_pid();

pg_backend_pid() schließt die eigene Session aus, sonst würdest du dich selbst beenden.

Datenbank droppen — alle Connections schließen

Wer DROP DATABASE myapp; ausführt, sieht häufig:

SQL
ERROR: database "myapp" is being accessed by other users
DETAIL: There are 3 other sessions using the database.

Erst alle Connections beenden, dann droppen:

SQL DB-Cleanup-Pattern
-- Verhindern, dass neue Connections aufgebaut werden:
REVOKE CONNECT ON DATABASE myapp FROM PUBLIC;

-- Bestehende Connections beenden:
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'myapp'
  AND pid <> pg_backend_pid();

-- Drop aus einer ANDEREN Datenbank heraus
-- (man kann eine DB nicht droppen, in der man verbunden ist):
\c postgres
DROP DATABASE myapp;

Ab PG 13 gibt es eine Abkürzung:

SQL
DROP DATABASE myapp WITH (FORCE);

FORCE beendet bestehende Connections automatisch und droppt dann. Praktisch, aber sollte nicht reflexartig genutzt werden — Datenverlust durch versehentliches Beenden von Production-Sessions.

Connection-Listen filtern

Häufige Filter:

SQL Idle-in-transaction laenger als 1min
SELECT pid, usename, datname,
       now() - state_change AS idle_for, query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
  AND now() - state_change > interval '1 minute';
SQL Connections gruppiert nach App
SELECT application_name, count(*) AS connections
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY application_name
ORDER BY connections DESC;

application_name lässt sich pro Connection vom Client setzen — z. B. in einer Postgres-URL: ?application_name=worker-1. Sehr nützlich, um in pg_stat_activity schnell zu sehen, welcher Service welche Sessions hält.

FAQ

Was ist der Unterschied zwischen pg_cancel und pg_terminate?

pg_cancel_backend bricht die laufende Query ab, lässt die Connection bestehen — der Client kann weitermachen. pg_terminate_backend schließt die Connection komplett — der Client bekommt einen Disconnect und muss sich neu verbinden. Wenn du nicht weißt, was du willst, fang mit pg_cancel an; wenn das nicht hilft, eskaliere auf pg_terminate.

Warum scheitert pg_terminate_backend manchmal mit „permission denied“?

Default ist: nur Superuser darf andere Sessions beenden. Wer ohne Superuser arbeiten will, braucht Mitgliedschaft in pg_signal_backend (Predefined Role). Ausnahme: die eigenen Sessions kannst du immer beenden, unabhängig von Privilegien.

idle in transaction — wie verhindere ich das?

idle_in_transaction_session_timeout setzt eine Obergrenze. Beispiel: ALTER ROLE app_user SET idle_in_transaction_session_timeout = '5min'; — länger offene Transaktionen werden vom Server abgebrochen. Standardmäßig auf 0 (deaktiviert), aber in Produktion meist eine gute Idee.

Kann ich eine bestimmte Query identifizieren, ohne die query-Spalte abzufragen?

pg_stat_activity.query zeigt das aktuell laufende Statement. Wenn du nur die letzte LANGE Query suchst, schau auf query_start — der Zeitpunkt, an dem das aktuelle Statement begonnen hat. state_change ist dagegen der Zeitpunkt, an dem der State zuletzt umgesprungen ist (wichtig bei idle in transaction).

Gibt es ein Web-Tool dafuer?

Ja — pgAdmin und DBeaver zeigen pg_stat_activity mit Buttons zum Cancelling/Terminating. Praktisch, aber für Skripte und Cron-Jobs ist die SQL-Variante zuverlässiger. In Notfall-Situationen lieber die Query auswendig kennen, als sich auf eine GUI zu verlassen, die gerade auch nicht reagiert.

DROP DATABASE … WITH (FORCE) ab PG 13.

Die WITH (FORCE)-Variante beendet alle Connections automatisch. Sehr praktisch in CI/Tests („nach jedem Lauf die Test-DB neu”). In Produktion mit Vorsicht — falls eine echte App-Connection drauf hängt, killt das die App.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Server-Administration

Zur Übersicht