Як скинути базу даних PostgreSQL, якщо до неї є активні підключення?


648

Мені потрібно написати сценарій, який видалить базу даних PostgreSQL. З цим може бути багато підключень, але сценарій повинен це ігнорувати.

Стандартний DROP DATABASE db_nameзапит не працює, коли є відкриті з'єднання.

Як я можу вирішити проблему?


1
На якій версії PostgreSQL ви працюєте?
Kuberchaun

1
Проблема. Хоча ви можете вбити сеанси, підключені до бази даних, вони можуть наново підключитися так швидко, що ви все ще не можете скинути базу даних. На щастя, у цій публікації показано, як заблокувати нові з’єднання, тож ви можете вбити поточні з'єднання та скинути базу даних за планом: dba.stackexchange.com/questions/11893/…
Макс Мерфі

1
Я знайшов цю відповідь на dba.stackexchange дуже корисною dba.stackexchange.com/a/11895/163539 - стислим, але достатньо пояснюючим.
hlongmore

Відповіді:


1093

Це призведе до відмови існуючих з’єднань, крім вашого:

Запитайте 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
І звичайно, не забудьте зробити це через db-з'єднання, яке не є з'єднанням з "TARGET_DB", інакше ви отримаєте "ПОМИЛКУ". З'єднання 'postgres' працює добре.
Роб

3
Насправді це відключить клієнтів один за одним, і якщо ви клієнт знаходиться в середині списку, він також буде відключений. Як результат, деякі зв’язки залишаться живими. Отже, правильна відповідь - Крейг Рінгер (див. Нижче). ВИБІРТЕ pg_terminate_backend (pg_stat_activity.pid) ВІД pg_stat_activity WHERE datname = current_database () AND pg_stat_activity.pid <> pg_backend_pid ();
Андрій Селіванов

1
Як я можу відключити з'єднання після того, як вони закінчили свою поточну трансакцію, а потім відкинути відповідну таблицю?
paulkon

5
У моєму випадку клієнти швидко підключиться, тому, ввівши це, перш ніж ; drop database TARGET_DB;добре попрацювати в моєму випадку, щоб переконатися, що 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
ВИБІРТЕ pg_terminate_backend (pg_stat_activity.pid) ВІД pg_stat_activity WHERE datname = current_database () AND 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 і змінити propid на pid.
MDR

2
Після зміни procpidв pidцьому фрагменті роботи на 9.3.
jb.

навіть не видаляючи pg_stat_activity? Я отримував помилку 9.2
MDR

ДОБРЕ. Тепер я розумію, що це була помилка друку. Дякую!
jb.

2
З 9.3 і вище SELECT pg_terminate_backend (pid) ВІД pg_stat_get_activity (NULL :: integer) WHERE datid = (SELECT oid від pg_database where datname = 'your_database');
Шон Вейдер

17

Я помітив, що postgres 9.2 тепер викликає pid стовпця, а не propid.

Я схильний називати це з оболонки:

#!/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/ перезапустити

введіть команду bg, щоб перевірити, чи інші процеси postgresql все ще запущені

потім слідує dropdb dbname для скидання бази даних

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

Це працює для мене в командному рядку Linux


6
Це не добре, якщо у вас багато баз даних і ви хочете перестати з'єднання для однієї БД. Це знищило б усі зв’язки. Це трохи "кувалда-у".
Нік

2
@Nick правда, але пам’ятайте, що ми відновлюємо всі з'єднання і повністю припиняємо їх
Моріс Елагу

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 скидає базу даних ... Крім того, якщо хто-небудь інший підключений до цільової бази даних, ця команда не вдасться, якщо ви не використовуєте параметр 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.