Come eliminare un database PostgreSQL se ci sono connessioni attive ad esso?


649

Devo scrivere uno script che lascerà cadere un database PostgreSQL. Potrebbero esserci molte connessioni, ma lo script dovrebbe ignorarlo.

La DROP DATABASE db_namequery standard non funziona quando ci sono connessioni aperte.

Come posso risolvere il problema?


1
Su quale versione di PostgreSQL utilizzi?
Kuberchaun,

1
Problema: sebbene sia possibile interrompere le sessioni connesse al database, è possibile che si riconnettano così rapidamente che non è ancora possibile eliminare il database. Fortunatamente questo post mostra come bloccare le nuove connessioni, quindi è possibile interrompere
Max Murphy

1
Ho trovato questa risposta su dba.stackexchange molto utile dba.stackexchange.com/a/11895/163539 - breve ma sufficientemente esplicativo.
hlongmore,

Risposte:


1094

Questo lascerà cadere le connessioni esistenti tranne le tue:

Interroga pg_stat_activitye ottieni i valori pid che desideri eliminare, quindi inviali SELECT pg_terminate_backend(pid int).

PostgreSQL 9.2 e versioni successive:

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 e versioni precedenti:

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();

Dopo aver disconnesso tutti, dovrai disconnetterti ed emettere il comando DROP DATABASE da una connessione da un altro database, non quella che stai tentando di eliminare.

Nota la ridenominazione della procpidcolonna in pid. Vedi questo thread della mailing list .


11
E, naturalmente, assicurati di farlo da una connessione db che non è una connessione a "TARGET_DB", altrimenti otterrai "ERRORE". Una connessione "postgres" funziona bene.
Rob,

3
In realtà, disconnetterebbe i client uno per uno e se il client si trova al centro dell'elenco verrà disconnesso. Di conseguenza, alcune connessioni rimarranno in vita. Quindi, la risposta giusta è di Craig Ringer (vedi sotto). SELEZIONA pg_terminate_backend (pg_stat_activity.pid) DA pg_stat_activity DOVE datname = current_database () E pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov,

1
Come posso disconnettere le connessioni dopo che hanno terminato con la loro transazione corrente e quindi eliminare le tabelle in questione?
paulkon,

5
Nel mio caso i clienti si riconnetterebbero rapidamente, quindi mettere questo poco prima ; drop database TARGET_DB;funzionava bene nel mio caso per assicurarsi che il db fosse sparito quando le cose hanno iniziato a riprovare.
Mat Schaffer,

1
Pagherei anche soldi per a dropdb --force.
Torsten Bronger

125

In PostgreSQL 9.2 e versioni successive, per disconnettere tutto tranne la sessione dal database a cui sei connesso:

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

Nelle versioni più vecchie è lo stesso, basta cambiare pida procpid. Per disconnettersi da un altro database basta cambiare current_database()il nome del database da cui si desidera disconnettere gli utenti.

Si consiglia REVOKEdella CONNECTdestra da parte degli utenti della banca dati prima di scollegare gli utenti, in caso contrario gli utenti sarà solo continuare a riconnessione e non sarai mai avere la possibilità di far cadere il DB. Vedi questo commento e la domanda a cui è associato, Come posso staccare tutti gli altri utenti dal database .

Se vuoi solo disconnettere gli utenti inattivi, vedi questa domanda .


3
SELEZIONA pg_terminate_backend (pg_stat_activity.pid) DA pg_stat_activity DOVE datname = current_database () E pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov,

26

È possibile interrompere tutte le connessioni prima di eliminare il database utilizzando la pg_terminate_backend(int)funzione.

Puoi ottenere tutti i backend in esecuzione utilizzando la vista di sistema pg_stat_activity

Non ne sono del tutto sicuro, ma quanto segue probabilmente ucciderebbe tutte le sessioni:

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

Ovviamente potresti non essere connesso a quel database


19

A seconda della versione di postgresql potresti imbatterti in un bug, questo rende pg_stat_activity che si omettano le connessioni attive dagli utenti abbandonati. Anche queste connessioni non sono mostrate all'interno di pgAdminIII.

Se stai eseguendo test automatici (in cui crei anche utenti) questo potrebbe essere uno scenario probabile.

In questo caso è necessario ripristinare query come:

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

NOTA: in 9.2+ dovrai cambiare procpidin pid.


1
Questo è quello che stavo cercando ma per (supponendo 9.2 e oltre) devi rimuovere il riferimento a pg_stat_activity e cambiare procpid in pid.
MDR

2
Dopo essere passato procpida pidquesto frammento funziona su 9.3.
jb.

anche senza rimuovere pg_stat_activity? Stavo ricevendo un errore il 9.2
MDR il

OK. Ora capisco, quello era un errore di battitura. Grazie!
jb.

2
Da 9.3 in poi SELEZIONARE pg_terminate_backend (pid) DA pg_stat_get_activity (NULL :: numero intero) WHERE datid = (SELECT oid da pg_database dove datname = 'your_database');
Shawn Vader,

17

Ho notato che Postgres 9.2 ora chiama la colonna pid anziché procpid.

Tendo a chiamarlo dalla shell:

#!/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

Spero sia utile. Grazie a @JustBob per il sql.


15

Ho appena riavviato il servizio in Ubuntu per disconnettere i client connessi.

sudo service postgresql stop
sudo service postgresql start

psql
DROP DATABASE DB_NAME;

10

Nel comando Linux Prompt, per prima cosa fermerei tutti i processi postgresql in esecuzione legando questo comando sudo /etc/init.d/postgresql restart

digitare il comando bg per verificare se altri processi postgresql sono ancora in esecuzione

quindi seguito da dbname dropdb per eliminare il database

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

Questo funziona per me sul prompt dei comandi di Linux


6
Questo non va bene se si dispone di molti database e si desidera eliminare solo le connessioni per un singolo DB. Ciò ucciderebbe tutte le connessioni. È un po '"mazza-y".
Nick,

2
@Nick true, ma ricorda che stiamo riavviando tutte le connessioni e interrompendole completamente
Maurice Elagu,

10

PostgreSQL 9.2 e versioni successive:

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


Non terminerà anche la connessione attiva?
Cocowalla,

8

Ecco il mio 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>;"

Ho inserito questa risposta perché includo un comando (sopra) per bloccare nuove connessioni e perché qualsiasi tentativo con il comando ...

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

... non funziona per bloccare nuove connessioni!

Grazie a @araqnid @GoatWalker! = D

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


5

Il prossimo PostgreSQL 13 introdurrà l' FORCEopzione.

DATABASE DROP

DROP DATABASE elimina un database ... Inoltre, se qualcun altro è connesso al database di destinazione, questo comando fallirà se non si utilizza l' opzione FORCE descritta di seguito.

VIGORE

Tentativo di terminare tutte le connessioni esistenti al database di destinazione. Non termina se nel database di destinazione sono presenti transazioni preparate, slot di replica logica attivi o abbonamenti.

DROP DATABASE db_name WITH (FORCE);

0

Nel mio caso ho dovuto eseguire un comando per eliminare tutte le connessioni inclusa la mia connessione di amministratore attiva

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

che ha interrotto tutte le connessioni e mi ha mostrato un messaggio fatale di "errore":

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

Successivamente è stato possibile eliminare il database


0

Niente ha funzionato per me, tranne che, ho effettuato l'accesso utilizzando pgAdmin4 e sul pannello di controllo ho disconnesso tutte le connessioni tranne pgAdmin4 e quindi sono stato in grado di rinominare leccando a destra sul database e le proprietà e ho digitato il nuovo nome.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.