Wie lösche ich eine PostgreSQL-Datenbank, wenn aktive Verbindungen zu ihr bestehen?

647

Ich muss ein Skript schreiben, das eine PostgreSQL-Datenbank löscht. Möglicherweise gibt es viele Verbindungen, aber das Skript sollte dies ignorieren.

Die Standardabfrage DROP DATABASE db_namefunktioniert nicht, wenn offene Verbindungen bestehen.

Wie kann ich das Problem lösen?

Roman Prykhodchenko
quelle
1
Auf welcher Version von PostgreSQL bist du?
Kuberchaun
1
Problem: Während Sie möglicherweise die mit der Datenbank verbundenen Sitzungen beenden, werden sie möglicherweise so schnell wieder verbunden, dass Sie die Datenbank immer noch nicht löschen können. Glücklicherweise zeigt dieser Beitrag, wie neue Verbindungen gesperrt werden
Max Murphy
1
Ich fand diese Antwort auf dba.stackexchange sehr hilfreich. Dba.stackexchange.com/a/11895/163539 - kurz und doch ausreichend erklärend.
Hlongmore

Antworten:

1092

Dadurch werden vorhandene Verbindungen außer Ihren getrennt:

Abfrage pg_stat_activityund erhalten Sie die pid Werte , die Sie töten wollen, dann erteilen SELECT pg_terminate_backend(pid int)ihnen.

PostgreSQL 9.2 und höher:

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'TARGET_DB' -- ← change this to your DB
  AND pid <> pg_backend_pid();

PostgreSQL 9.1 und niedriger:

SELECT pg_terminate_backend(pg_stat_activity.procpid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'TARGET_DB' -- ← change this to your DB
  AND procpid <> pg_backend_pid();

Sobald Sie alle getrennt haben, müssen Sie den Befehl DROP DATABASE von einer Verbindung aus einer anderen Datenbank trennen und ausgeben, auch bekannt als nicht die, die Sie löschen möchten.

Beachten Sie die Umbenennung der procpidSpalte in pid. Siehe diesen Mailinglisten-Thread .

Kuberchaun
quelle
11
Und stellen Sie sicher, dass Sie dies von einer Datenbankverbindung aus tun, die keine Verbindung zu 'TARGET_DB' ist, andernfalls erhalten Sie 'ERROR'. Eine Postgres-Verbindung funktioniert gut.
Rob
3
Tatsächlich werden Clients nacheinander getrennt, und wenn sich Ihr Client in der Mitte der Liste befindet, wird er ebenfalls getrennt. Infolgedessen bleiben einige Verbindungen am Leben. Die richtige Antwort stammt also von Craig Ringer (siehe unten). SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE datname = current_database () AND pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov
1
Wie kann ich die Verbindungen trennen, nachdem sie ihre aktuelle Transaktion abgeschlossen haben, und dann die fraglichen Tabellen löschen?
Paulkon
5
In meinem Fall haben sich die Clients schnell wieder verbunden, daher hat es ; drop database TARGET_DB;in meinem Fall gut funktioniert , dies kurz zuvor zu tun, um sicherzustellen, dass die Datenbank zu dem Zeitpunkt, als die Dinge erneut versucht wurden, verschwunden war.
Mat Schaffer
1
Ich würde sogar Geld für eine bezahlen dropdb --force.
Torsten Bronger
125

In PostgreSQL 9.2 und höher können Sie alle Verbindungen außer Ihrer Sitzung von der Datenbank trennen, mit der Sie verbunden sind:

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE datname = current_database()
  AND pid <> pg_backend_pid();

In älteren Versionen ist es dasselbe, wechseln Sie einfach pidzu procpid. Um die Verbindung zu einer anderen Datenbank zu trennen, ändern current_database()Sie einfach den Namen der Datenbank, von der Sie die Verbindung trennen möchten.

Möglicherweise möchten Sie sich von REVOKEden CONNECTBenutzern der Datenbank abheben, bevor Sie die Verbindung zu den Benutzern trennen. Andernfalls stellen die Benutzer die Verbindung einfach wieder her und Sie haben nie die Möglichkeit, die Datenbank zu löschen. Sehen Sie diesen Kommentar und die Frage dem er zugeordnet ist, Wie kann ich lösen alle anderen Benutzer aus der Datenbank .

Wenn Sie nur inaktive Benutzer trennen möchten, lesen Sie diese Frage .

Craig Ringer
quelle
3
SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE datname = current_database () AND pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov
26

Sie können alle Verbindungen beenden, bevor Sie die Datenbank mit der pg_terminate_backend(int)Funktion löschen.

Sie können alle laufenden Backends über die Systemansicht abrufen pg_stat_activity

Ich bin nicht ganz sicher, aber das Folgende würde wahrscheinlich alle Sitzungen beenden:

select pg_terminate_backend(procpid)
from pg_stat_activity
where datname = 'doomed_database'

Natürlich sind Sie möglicherweise nicht selbst mit dieser Datenbank verbunden

ein Pferd ohne Name
quelle
19

Abhängig von Ihrer Version von postgresql kann es zu einem Fehler kommen, der pg_stat_activitydazu führt , dass aktive Verbindungen von abgelegten Benutzern weggelassen werden. Diese Verbindungen werden auch in pgAdminIII nicht angezeigt.

Wenn Sie automatische Tests durchführen (in denen Sie auch Benutzer erstellen), ist dies möglicherweise ein wahrscheinliches Szenario.

In diesem Fall müssen Sie zu folgenden Abfragen zurückkehren:

 SELECT pg_terminate_backend(procpid) 
 FROM pg_stat_get_activity(NULL::integer) 
 WHERE datid=(SELECT oid from pg_database where datname = 'your_database');

HINWEIS: In 9.2+ Sie ändern müssen procpidzu pid.

jb.
quelle
1
Das ist, wonach ich gesucht habe, aber für (vorausgesetzt 9.2 und darüber hinaus) müssen Sie den Verweis auf pg_stat_activity entfernen und procpid in pid ändern.
MDR
2
Nach dem Wechsel procpidzu piddiesem Snippet funktioniert 9.3.
jb.
auch ohne pg_stat_activity zu entfernen? Ich habe einen Fehler am 9.2
MDR
OK. Jetzt verstehe ich, das war ein Tippfehler. Vielen Dank!
jb.
2
Ab 9.3 SELECT pg_terminate_backend (pid) FROM pg_stat_get_activity (NULL :: integer) WHERE datid = (SELECT oid from pg_database wobei datname = 'your_database');
Shawn Vader
17

Mir ist aufgefallen, dass Postgres 9.2 jetzt eher die Spalten-PID als die Procpid aufruft.

Ich neige dazu, es aus der Shell zu nennen:

#!/usr/bin/env bash
# kill all connections to the postgres server
if [ -n "$1" ] ; then
  where="where pg_stat_activity.datname = '$1'"
  echo "killing all connections to database '$1'"
else
  echo "killing all connections to database"
fi

cat <<-EOF | psql -U postgres -d postgres 
SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
${where}
EOF

Hoffe das ist hilfreich. Danke an @JustBob für die SQL.

kbrock
quelle
15

Ich starte den Dienst in Ubuntu einfach neu, um verbundene Clients zu trennen.

sudo service postgresql stop
sudo service postgresql start

psql
DROP DATABASE DB_NAME;
devdrc
quelle
10

In der Linux-Eingabeaufforderung würde ich zuerst alle ausgeführten postgresql-Prozesse stoppen, indem ich diesen Befehl verknüpfe sudo /etc/init.d/postgresql restart verknüpfe

Geben Sie den Befehl bg einGeben Sie , um zu überprüfen, ob noch andere postgresql-Prozesse ausgeführt werden

dann gefolgt von dropdb dbname , um die Datenbank zu löschen

sudo /etc/init.d/postgresql restart
bg
dropdb dbname

Dies funktioniert bei mir an der Linux-Eingabeaufforderung

Maurice Elagu
quelle
6
Dies ist nicht gut, wenn Sie über viele Datenbanken verfügen und nur Verbindungen für eine einzelne Datenbank trennen möchten. Dies würde alle Verbindungen beenden. Es ist ein bisschen "Vorschlaghammer".
Nick
2
@ Nick wahr, aber denken Sie daran, wir starten alle Verbindungen neu und stoppen sie vollständig
Maurice Elagu
10

PostgreSQL 9.2 und höher:

SELECT pg_terminate_backend(pid)FROM pg_stat_activity WHERE datname = 'YOUR_DATABASE_NAME_HERE'

Marcelo C.
quelle
Wird das nicht auch die aktive Verbindung beenden?
Cocowalla
8

Hier ist mein Hack ... = D.

# Make sure no one can connect to this database except you!
sudo -u postgres /usr/pgsql-9.4/bin/psql -c "UPDATE pg_database SET datallowconn=false WHERE datname='<DATABASE_NAME>';"

# Drop all existing connections except for yours!
sudo -u postgres /usr/pgsql-9.4/bin/psql -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '<DATABASE_NAME>' AND pid <> pg_backend_pid();"

# Drop database! =D
sudo -u postgres /usr/pgsql-9.4/bin/psql -c "DROP DATABASE <DATABASE_NAME>;"

Ich habe diese Antwort gegeben, weil ich einen Befehl (oben) zum Blockieren neuer Verbindungen eingefügt habe und weil jeder Versuch mit dem Befehl ...

REVOKE CONNECT ON DATABASE <DATABASE_NAME> FROM PUBLIC, <USERS_ETC>;

... funktioniert nicht, um neue Verbindungen zu blockieren!

Vielen Dank an @araqnid @GoatWalker! = D.

https://stackoverflow.com/a/3185413/3223785

Eduardo Lucio
quelle
5

Das kommende PostgreSQL 13 wird die FORCEOption einführen .

DROP DATABASE

DROP DATABASE löscht eine Datenbank ... Wenn jemand anderes mit der Zieldatenbank verbunden ist, schlägt dieser Befehl fehl, es sei denn, Sie verwenden die unten beschriebene Option FORCE .

MACHT

Versuchen Sie, alle vorhandenen Verbindungen zur Zieldatenbank zu beenden. Es wird nicht beendet, wenn vorbereitete Transaktionen, aktive logische Replikationssteckplätze oder Abonnements in der Zieldatenbank vorhanden sind.

DROP DATABASE db_name WITH (FORCE);
Lukasz Szozda
quelle
0

In meinem Fall musste ich einen Befehl ausführen, um alle Verbindungen einschließlich meiner aktiven Administratorverbindung zu trennen

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE datname = current_database()

Das hat alle Verbindungen beendet und mir eine schwerwiegende Fehlermeldung angezeigt:

FATAL: terminating connection due to administrator command SQL state: 57P01

Danach war es möglich, die Datenbank zu löschen

Chtiwi Malek
quelle
0

Bei mir hat nichts funktioniert, außer ich habe mich mit pgAdmin4 angemeldet und im Dashboard alle Verbindungen außer pgAdmin4 getrennt und konnte dann durch Klicken mit der rechten Maustaste auf die Datenbank und die Eigenschaften umbenennen und einen neuen Namen eingeben.

Ashburn RK
quelle