¿Cómo descartar una base de datos PostgreSQL si hay conexiones activas?

650

Necesito escribir un script que suelte una base de datos PostgreSQL. Puede haber muchas conexiones, pero el script debe ignorar eso.

La DROP DATABASE db_nameconsulta estándar no funciona cuando hay conexiones abiertas.

¿Cómo puedo resolver el problema?

Roman Prykhodchenko
fuente
1
¿En qué versión de PostgreSQL estás?
Kuberchaun
1
Problema: si bien puede eliminar las sesiones conectadas a la base de datos, es posible que se vuelvan a conectar tan rápido que aún no pueda soltar la base de datos. Felizmente, esta publicación muestra cómo bloquear nuevas conexiones, para que luego pueda eliminar las conexiones actuales y soltar la base de datos según el plan: dba.stackexchange.com/questions/11893/…
Max Murphy
1
Encontré esta respuesta en dba.stackexchange muy útil dba.stackexchange.com/a/11895/163539 - sucinta pero suficientemente explicativa.
hlongmore

Respuestas:

1095

Esto eliminará las conexiones existentes, excepto las suyas:

Consulte pg_stat_activityy obtenga los valores pid que desea eliminar, luego emítalos SELECT pg_terminate_backend(pid int).

PostgreSQL 9.2 y superior:

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 y siguientes:

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

Una vez que desconecte a todos, deberá desconectar y emitir el comando DROP DATABASE desde una conexión desde otra base de datos, es decir, no la que está tratando de eliminar.

Tenga en cuenta el cambio de nombre de la procpidcolumna a pid. Vea este hilo de la lista de correo .

Kuberchaun
fuente
11
Y, por supuesto, asegúrese de hacerlo desde una conexión db que no sea una conexión a 'TARGET_DB', de lo contrario obtendrá 'ERROR'. Una conexión 'postgres' funciona bien.
Rob
3
En realidad, desconectaría a los clientes uno por uno, y si su cliente está en el medio de la lista, también se desconectará. Como resultado, algunas conexiones permanecerán vivas. Entonces, la respuesta correcta es Craig Ringer (ver más abajo). SELECCIONE pg_terminate_backend (pg_stat_activity.pid) DESDE pg_stat_activity DONDE datname = current_database () Y pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov
1
¿Cómo puedo desconectar las conexiones después de que hayan terminado con su transacción actual y luego descartar las tablas en cuestión?
paulkon
55
En mi caso, los clientes se volverían a conectar rápidamente, por lo que poner esto justo antes ; drop database TARGET_DB;funcionó bien en mi caso para asegurarme de que la base de datos se hubiera ido para cuando las cosas comenzaran a reintentarse.
Mat Schaffer
1
Incluso pagaría dinero por un dropdb --force.
Torsten Bronger
125

En PostgreSQL 9.2 y superior, para desconectar todo excepto su sesión de la base de datos a la que está conectado:

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

En versiones anteriores es lo mismo, solo cambie pida procpid. Para desconectarse de una base de datos diferente, simplemente cambie current_database()el nombre de la base de datos de la que desea desconectar a los usuarios.

Es posible que desee a REVOKEla CONNECTderecha de los usuarios de la base de datos antes de desconectar a los usuarios, de lo contrario, los usuarios simplemente se volverán a conectar y nunca tendrán la oportunidad de abandonar la base de datos. Vea este comentario y la pregunta con la que está asociado, ¿Cómo desconecto a todos los demás usuarios de la base de datos ?

Si solo desea desconectar a los usuarios inactivos, consulte esta pregunta .

Craig Ringer
fuente
3
SELECCIONE pg_terminate_backend (pg_stat_activity.pid) DESDE pg_stat_activity DONDE datname = current_database () Y pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov
26

Puede eliminar todas las conexiones antes de descartar la base de datos utilizando la pg_terminate_backend(int)función.

Puede obtener todos los backends en ejecución usando la vista del sistema pg_stat_activity

No estoy completamente seguro, pero lo siguiente probablemente mataría todas las sesiones:

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

Por supuesto, es posible que no estés conectado a esa base de datos

un caballo sin nombre
fuente
19

Dependiendo de su versión de postgresql, puede encontrarse con un error, que hace pg_stat_activityque omita las conexiones activas de los usuarios descartados. Estas conexiones tampoco se muestran dentro de pgAdminIII.

Si está realizando pruebas automáticas (en las que también crea usuarios), este podría ser un escenario probable.

En este caso, debe volver a consultas como:

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

NOTA: En 9.2+ tendrás que cambiar procpida pid.

jb.
fuente
1
Esto es lo que estaba buscando, pero (suponiendo 9.2 y más allá) debe eliminar la referencia a pg_stat_activity y cambiar procpid a pid.
MDR
2
Después de cambiar procpida pideste fragmento funciona en 9.3.
jb.
incluso sin eliminar pg_stat_activity? Recibí un error el 9.2
MDR
OKAY. Ahora entiendo, eso fue un error tipográfico. ¡Gracias!
jb.
2
Desde 9.3 en adelante SELECCIONE pg_terminate_backend (pid) FROM pg_stat_get_activity (NULL :: integer) WHERE datid = (SELECCIONE oid desde pg_database donde datname = 'your_database');
Shawn Vader
17

Noté que postgres 9.2 ahora llama a la columna pid en lugar de procpid.

Tiendo a llamarlo desde la cáscara:

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

Espero que sea útil. Gracias a @JustBob por el sql.

kbrock
fuente
15

Acabo de reiniciar el servicio en Ubuntu para desconectar los clientes conectados.

sudo service postgresql stop
sudo service postgresql start

psql
DROP DATABASE DB_NAME;
devdrc
fuente
10

En el símbolo del sistema de Linux, primero detendría todos los procesos postgresql que se están ejecutando al vincular este comando sudo /etc/init.d/postgresql restart

escriba el comando bg para verificar si otros procesos postgresql todavía se están ejecutando

luego seguido por dropdb dbname para descartar la base de datos

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

Esto funciona para mí en el símbolo del sistema de Linux

Maurice Elagu
fuente
66
Esto no es bueno si tiene muchas bases de datos y solo desea desconectar las conexiones para una única base de datos. Esto mataría todas las conexiones. Es un poco "trineo martillo".
Nick
2
@Nick true pero recuerda que estamos reiniciando todas las conexiones y deteniéndolas por completo
Maurice Elagu
10

PostgreSQL 9.2 y superior:

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

Marcelo C.
fuente
¿Eso no terminará la conexión activa también?
Cocowalla
8

Aquí está mi truco ... = 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>;"

Pongo esta respuesta porque incluye un comando (arriba) para bloquear nuevas conexiones y porque cualquier intento con el comando ...

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

... no funciona para bloquear nuevas conexiones!

¡Gracias a @araqnid @GoatWalker! = D

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

Eduardo Lucio
fuente
5

Próximamente PostgreSQL 13 presentará la FORCEopción.

DROP DATABASE

DROP DATABASE descarta una base de datos ... Además, si alguien más está conectado a la base de datos de destino, este comando fallará a menos que use la opción FORCE que se describe a continuación.

FUERZA

Intente terminar todas las conexiones existentes a la base de datos de destino. No termina si las transacciones preparadas, las ranuras de replicación lógica activa o las suscripciones están presentes en la base de datos de destino.

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

En mi caso, tuve que ejecutar un comando para desconectar todas las conexiones, incluida mi conexión de administrador activa

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

que finalizó todas las conexiones y me mostró un mensaje fatal de "error":

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

Después de eso, fue posible soltar la base de datos

Chtiwi Malek
fuente
0

Nada funcionó para mí, excepto que inicié sesión usando pgAdmin4 y en el Tablero desconecté todas las conexiones excepto pgAdmin4 y luego pude cambiar el nombre haciendo clic con el botón derecho en la base de datos y las propiedades y escribí un nuevo nombre.

Ashburn RK
fuente