Jak usunąć bazę danych PostgreSQL, jeśli istnieją do niej aktywne połączenia?

648

Muszę napisać skrypt, który upuści bazę danych PostgreSQL. Może być z nim wiele połączeń, ale skrypt powinien to zignorować.

Standardowe DROP DATABASE db_namezapytanie nie działa, gdy istnieją otwarte połączenia.

Jak mogę rozwiązać problem?

Roman Prykhodchenko
źródło
1
W jakiej wersji PostgreSQL używasz?
Kuberchaun
1
Problem: Chociaż możesz zabić sesje podłączone do bazy danych, mogą one zostać ponownie połączone tak szybko, że nadal nie możesz upuścić bazy danych. Na szczęście ten post pokazuje, jak zablokować nowe połączenia, abyś mógł następnie zabić obecne połączenia i upuścić bazę danych zgodnie z planem: dba.stackexchange.com/questions/11893/…
Max Murphy
1
Przekonałem się, że ta odpowiedź na dba.stackexchange jest bardzo pomocna dba.stackexchange.com/a/11895/163539 - zwięzła, ale wystarczająco wyjaśniona.
hlongmore

Odpowiedzi:

1093

Spowoduje to porzucenie istniejących połączeń oprócz twoich:

Zapytaj pg_stat_activityi uzyskaj wartości pid, które chcesz zabić, a następnie wydaj SELECT pg_terminate_backend(pid int)je.

PostgreSQL 9.2 i nowsze wersje:

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 i poniżej:

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

Gdy rozłączysz wszystkich, będziesz musiał rozłączyć i wydać polecenie DROP DATABASE z połączenia z innej bazy danych, inaczej niż ta, którą próbujesz usunąć.

Zwróć uwagę na zmianę nazwy procpidkolumny na pid. Zobacz ten wątek listy adresowej .

Kuberchaun
źródło
11
I oczywiście pamiętaj, aby to zrobić z połączenia db, które nie jest połączeniem z „TARGET_DB”, w przeciwnym razie otrzymasz „ERROR”. Połączenie „postgres” działa dobrze.
Rob
3
W rzeczywistości rozłączałby on klientów jeden po drugim, a jeśli klient znajduje się na środku listy, również zostanie odłączony. W rezultacie niektóre połączenia pozostaną przy życiu. Tak więc poprawną odpowiedzią jest Craig Ringer (patrz poniżej). WYBIERZ pg_terminate_backend (pg_stat_activity.pid) OD pg_stat_activity GDZIE nazwa_danych = bieżąca_bazy danych () ORAZ pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov,
1
Jak mogę rozłączyć połączenia po zakończeniu ich bieżącej transakcji, a następnie upuścić dane tabele?
paulkon
5
W moim przypadku klienci szybko nawiążą ponowne połączenie, więc umieszczenie tego tuż przedtem ; drop database TARGET_DB;działało dobrze w moim przypadku, aby upewnić się, że baza danych zniknęła, zanim wszystko zacznie się ponawiać.
Mat Schaffer,
1
Zapłaciłbym nawet za pieniądze dropdb --force.
Torsten Bronger
125

W PostgreSQL 9.2 i nowszych, aby odłączyć wszystko oprócz sesji od bazy danych, z którą jesteś połączony:

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

W starszych wersjach jest tak samo, wystarczy zmienić pidna procpid. Aby rozłączyć się z inną bazą danych, wystarczy zmienić current_database()nazwę bazy danych, z której chcesz rozłączyć użytkowników.

Możesz REVOKEna CONNECTprawo od użytkowników bazy danych przed odłączeniem użytkowników, inaczej użytkownicy będą po prostu zachować na ponownym podłączeniu i nigdy nie będziesz miał szansę na wyrzucenie DB. Zobacz ten komentarz i pytanie z nim związane: Jak odłączyć wszystkich innych użytkowników od bazy danych .

Jeśli chcesz tylko odłączyć bezczynnych użytkowników, zobacz to pytanie .

Craig Ringer
źródło
3
WYBIERZ pg_terminate_backend (pg_stat_activity.pid) OD pg_stat_activity GDZIE nazwa_danych = bieżąca_bazy danych () ORAZ pg_stat_activity.pid <> pg_backend_pid ();
Andrew Selivanov,
26

Możesz usunąć wszystkie połączenia przed usunięciem bazy danych za pomocą pg_terminate_backend(int)funkcji.

Możesz uzyskać wszystkie działające backendy za pomocą widoku systemu pg_stat_activity

Nie jestem do końca pewien, ale następujące działania prawdopodobnie zabiłyby wszystkie sesje:

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

Oczywiście możesz nie być podłączony do tej bazy danych

koń bez imienia
źródło
19

W zależności od wersji postgresql możesz napotkać błąd, który powoduje pg_stat_activity pominięcie aktywnych połączeń od upuszczonych użytkowników. Te połączenia nie są również pokazane w pgAdminIII.

Jeśli wykonujesz testy automatyczne (w których również tworzysz użytkowników), może to być prawdopodobny scenariusz.

W takim przypadku musisz wrócić do zapytań takich jak:

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

UWAGA: W wersji 9.2+ zmienisz procpidna pid.

jb.
źródło
1
Właśnie tego szukałem, ale (zakładając, że 9.2 i więcej) musisz usunąć odniesienie do pg_stat_activity i zmienić procpid na pid.
MDR,
2
Po zmianie procpidna pidten fragment działa na 9.3.
jb.
nawet bez usuwania pg_stat_activity? Otrzymałem błąd w dniu 9.2
MDR
OK. Teraz rozumiem, to była literówka. Dzięki!
jb.
2
Począwszy od 9.3 WYBIERZ pg_terminate_backend (pid) OD pg_stat_get_activity (NULL :: liczba całkowita) GDZIE datid = (WYBIERZ oid z pg_database gdzie datname = 'twoja_bazy danych');
Shawn Vader
17

Zauważyłem, że postgres 9.2 teraz nazywa kolumnę pid zamiast procpid.

Zwykle nazywam to z powłoki:

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

Mam nadzieję, że jest to pomocne. Dzięki @JustBob dla sql.

kbrock
źródło
15

Właśnie uruchamiam ponownie usługę w Ubuntu, aby rozłączyć podłączonych klientów.

sudo service postgresql stop
sudo service postgresql start

psql
DROP DATABASE DB_NAME;
devdrc
źródło
10

W wierszu polecenia systemu Linux najpierw zatrzymałbym wszystkie uruchomione procesy postgresql, wiążąc to polecenie sudo /etc/init.d/postgresql restart

wpisz polecenie bg, aby sprawdzić, czy inne procesy postgresql nadal działają

a następnie dropdb dbname, aby usunąć bazę danych

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

Działa to dla mnie w wierszu poleceń systemu Linux

Maurice Elagu
źródło
6
Nie jest to dobre, jeśli masz wiele baz danych i chcesz porzucić połączenia tylko dla jednej bazy danych. To zabiłoby wszystkie połączenia. To trochę „młot kowalski”.
Nick
2
@Nick prawda, ale pamiętajmy, że restartujemy wszystkie połączenia i całkowicie je zatrzymujemy
Maurice Elagu
10

PostgreSQL 9.2 i nowsze wersje:

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

Marcelo C.
źródło
Czy to też nie zakończy aktywnego połączenia?
Cocowalla
8

Oto mój 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>;"

Podaję tę odpowiedź, ponieważ zawierają polecenie (powyżej), aby zablokować nowe połączenia i ponieważ każda próba użycia polecenia ...

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

... nie działa, aby blokować nowe połączenia!

Dzięki @araqnid @GoatWalker! = D.

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

Eduardo Lucio
źródło
5

Nadchodzące PostgreSQL 13 wprowadzi FORCEopcję.

DROP DATABASE

DROP DATABASE upuszcza bazę danych ... Również jeśli ktoś inny jest podłączony do docelowej bazy danych, to polecenie zakończy się niepowodzeniem, chyba że użyjesz opcji FORCE opisanej poniżej.

SIŁA

Próba zakończenia wszystkich istniejących połączeń z docelową bazą danych. Nie kończy się, jeśli w docelowej bazie danych znajdują się przygotowane transakcje, aktywne logiczne gniazda replikacji lub subskrypcje.

DROP DATABASE db_name WITH (FORCE);
Łukasz Szozda
źródło
0

W moim przypadku musiałem wykonać polecenie, aby usunąć wszystkie połączenia, w tym moje aktywne połączenie administratora

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

który zakończył wszystkie połączenia i pokazał mi krytyczny komunikat „błąd”:

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

Następnie można było usunąć bazę danych

Chtiwi Malek
źródło
0

Nic mi nie działało, oprócz tego, że zalogowałem się za pomocą pgAdmin4, a na pulpicie nawigacyjnym odłączyłem wszystkie połączenia oprócz pgAdmin4, a następnie mogłem zmienić nazwę, klikając prawym przyciskiem myszy bazę danych i właściwości, i wpisałem nową nazwę.

Ashburn RK
źródło