Как удалить базу данных PostgreSQL, если к ней есть активные подключения?


650

Мне нужно написать скрипт, который удалит базу данных PostgreSQL. С ним может быть много связей, но скрипт должен это игнорировать.

Стандартный DROP DATABASE db_nameзапрос не работает при открытых соединениях.

Как я могу решить проблему?


1
На какой версии PostgreSQL вы работаете?
Kuberchaun

1
Проблема: хотя вы можете завершить сеансы, подключенные к базе данных, они могут восстановить соединение так быстро, что вы все равно не сможете удалить базу данных. К счастью, в этом посте показано, как заблокировать новые подключения, чтобы затем можно было отключить
Макс Мерфи,

1
Я нашел этот ответ на dba.stackexchange очень полезным dba.stackexchange.com/a/11895/163539 - кратким, но достаточно объяснительным.
Хлонгмор

Ответы:


1095

Это приведет к удалению существующих подключений, кроме вашего:

Запросите pg_stat_activityи получите значения pid, которые хотите убить, а затем введите SELECT pg_terminate_backend(pid int)их.

PostgreSQL 9.2 и выше:

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 и ниже:

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

После того, как вы отключите всех, вам придется отключить и выполнить команду DROP DATABASE из соединения с другой базой данных, а не той, которую вы пытаетесь сбросить.

Обратите внимание на переименование procpidстолбца в pid. Смотрите эту ветку списка рассылки .


11
И, конечно же, обязательно сделайте это из соединения с БД, которое не является соединением с «TARGET_DB», в противном случае вы получите «ОШИБКУ». Соединение «postgres» работает хорошо.
Роб

3
На самом деле это будет отключать клиентов один за другим, и если ваш клиент находится в середине списка, он также будет отключен. В результате некоторые соединения останутся живыми. Итак, правильный ответ - Крейг Рингер (см. Ниже). SELECT
Андрей Селиванов

1
Как я могу отключить соединения после того, как они закончили свою текущую транзакцию, а затем удалить соответствующие таблицы?
Полкон

5
В моем случае клиенты быстро восстановили бы соединение, поэтому ; drop database TARGET_DB;в моем случае это работало хорошо, чтобы убедиться, что база данных исчезла к тому времени, когда все начало повторяться.
Мат Шаффер

1
Я бы даже за деньги заплатил dropdb --force.
Торстен Бронджер

125

В PostgreSQL 9.2 и выше отключить все, кроме сеанса, от базы данных, к которой вы подключены:

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

В старых версиях то же самое, просто измените pidна procpid. Чтобы отключиться от другой базы данных, просто измените current_database()имя базы данных, от которой вы хотите отключить пользователей.

Вы можете REVOKEв CONNECTправе от пользователей базы данных перед отключением пользователей, в противном случае пользователи будут просто держать на Reconnecting и вы никогда не получите шанс понизить БД. Посмотрите этот комментарий и вопрос, с которым он связан, как мне отсоединить всех других пользователей от базы данных .

Если вы просто хотите отключить незанятых пользователей, посмотрите этот вопрос .


3
SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE datname = current_database () И pg_stat_activity.pid <> pg_backend_pid ();
Андрей Селиванов

26

Вы можете уничтожить все соединения перед удалением базы данных, используя pg_terminate_backend(int)функцию.

Вы можете получить все запущенные бэкэнды, используя системное представление pg_stat_activity

Я не совсем уверен, но следующее, вероятно, убьет все сессии:

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

Конечно, вы не можете быть подключены к этой базе данных


19

В зависимости от вашей версии postgresql вы можете столкнуться с ошибкой, которая делает pg_stat_activity за которой пропускаются активные соединения от сброшенных пользователей. Эти соединения также не показаны внутри pgAdminIII.

Если вы выполняете автоматическое тестирование (в котором вы также создаете пользователей), это может быть вероятным сценарием.

В этом случае вам нужно вернуться к таким запросам, как:

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

ПРИМЕЧАНИЕ. В версии 9.2+ вам нужно изменить procpidна pid.


1
Это то, что я искал, но (при условии 9.2 и выше) вы должны удалить ссылку на pg_stat_activity и изменить procpid на pid.
MDR

2
После перехода procpidна pidэтот фрагмент работает на 9.3.
JB.

даже без удаления pg_stat_activity? Я получил сообщение об ошибке 9.2
MDR

ХОРОШО. Теперь я понимаю, что это была опечатка. Спасибо!
JB.

2
От 9.3 и более
Шон Вейдер

17

Я заметил, что postgres 9.2 теперь вызывает pid столбца, а не procpid.

Я склонен называть это из оболочки:

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

Надеюсь, что это полезно. Спасибо @JustBob за sql.


15

Я просто перезапускаю сервис в Ubuntu, чтобы отключить подключенных клиентов.

sudo service postgresql stop
sudo service postgresql start

psql
DROP DATABASE DB_NAME;

10

В командной строке Linux я бы сначала остановил все запущенные процессы postgresql, связав эту команду sudo /etc/init.d/postgresql restart

введите команду bg, чтобы проверить, работают ли другие процессы postgresql

затем следует dropdb dbname для удаления базы данных

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

Это работает для меня в командной строке Linux


6
Это не хорошо, если у вас много баз данных и вы хотите сбросить соединения только для одной БД. Это убило бы все соединения. Это немного "кувалда-у".
Ник

2
@ Ник верно, но помните, что мы перезапускаем все соединения и полностью их прекращаем
Морис Элагу

10

PostgreSQL 9.2 и выше:

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


Разве это не прекратит активную связь тоже?
Cocowalla

8

Вот мой хак ... = 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>;"

Я поставил этот ответ, потому что включить команду (выше), чтобы заблокировать новые подключения и потому, что любая попытка с командой ...

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

... не работает, чтобы заблокировать новые подключения!

Спасибо @araqnid @GoatWalker! = D

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


5

Предстоящий PostgreSQL 13 представит FORCEопцию.

DROP DATABASE

DROP DATABASE удаляет базу данных ... Кроме того, если кто-либо еще подключен к целевой базе данных, эта команда не будет выполнена, если вы не используете опцию FORCE, описанную ниже.

FORCE

Попытайтесь разорвать все существующие соединения с целевой базой данных. Он не завершается, если в целевой базе данных присутствуют подготовленные транзакции, активные слоты логической репликации или подписки.

DROP DATABASE db_name WITH (FORCE);

0

В моем случае мне пришлось выполнить команду, чтобы сбросить все соединения, включая мое активное соединение администратора

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

который разорвал все соединения и показал мне фатальное сообщение об ошибке:

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

После этого можно было сбросить базу данных


0

У меня ничего не получалось, за исключением того, что я вошел в систему с помощью pgAdmin4, и на панели инструментов я отключил все соединения, кроме pgAdmin4, а затем смог переименовать его, щелкнув правой кнопкой мыши на базе данных и свойствах, и набрал новое имя.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.