web-dev-qa-db-fra.com

Forcer la baisse de la base de données tandis que d'autres peuvent être connectés

J'ai besoin de supprimer une base de données d'un cluster de bases de données PostgreSQL. Comment puis-je le faire même s'il y a des connexions actives? J'ai besoin d'une sorte de -force flag, qui supprimera toutes les connexions, puis la base de données.

Comment puis-je l'implémenter?

J'utilise dropdb actuellement, mais d'autres outils sont possibles.

126
Alex

Dans PostgreSQL*, vous ne pouvez pas supprimer une base de données pendant que les clients y sont connectés.

Du moins, pas avec l'utilitaire dropdb - qui n'est qu'un simple wrapper DROP DATABASE requête du serveur.

Une solution de contournement assez robuste suit:

Connectez-vous à votre serveur en tant que superutilisateur , en utilisant psql ou un autre client. N'utilisez pas la base de données que vous souhaitez supprimer.

psql -h localhost postgres postgres

Maintenant, en utilisant le client de base de données ordinaire, vous pouvez forcer la suppression de la base de données en trois étapes simples:

  1. Assurez-vous que personne ne peut se connecter à cette base de données. Vous pouvez utiliser l'une des méthodes suivantes (la seconde semble plus sûre, mais n'empêche pas les connexions des superutilisateurs).

    /* Method 1: update system catalog */
    UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'mydb';
    
    /* Method 2: use ALTER DATABASE. Superusers still can connect!
    ALTER DATABASE mydb CONNECTION LIMIT 0; */
    
  2. Forcer la déconnexion de tous les clients connectés à cette base de données, à l'aide de pg_terminate_backend .

    SELECT pg_terminate_backend(pid)
    FROM pg_stat_activity
    WHERE datname = 'mydb';
    
    /* For old versions of PostgreSQL (up to 9.1), change pid to procpid:
    
    SELECT pg_terminate_backend(procpid)
    FROM pg_stat_activity
    WHERE datname = 'mydb'; */
    
  3. Supprimez la base de données.

    DROP DATABASE mydb;
    

L'étape 1 nécessite les privilèges de superutilisateur pour la 1ère méthode et les privilèges du propriétaire de la base de données pour le 2ème. L'étape 2 nécessite les privilèges de superutilisateur . L'étape 3 nécessite le privilège du propriétaire de la base de données .


Cela s'applique à toutes les versions de PostgreSQL, jusqu'à la version 12. La version 13 a DROP DATABASE mydb FORCE


178
filiprem

Utiliser la réponse de @ filiprem dans mon cas et la simplifier:

-- Connecting to the current user localhost's postgres instance
psql

-- Making sure the database exists
SELECT * from pg_database where datname = 'my_database_name'

-- Disallow new connections
UPDATE pg_database SET datallowconn = 'false' WHERE datname = 'my_database_name';
ALTER DATABASE my_database_name CONNECTION LIMIT 1;

-- Terminate existing connections
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'my_database_name';

-- Drop database
DROP DATABASE my_database_name
9
Dorian

Il y a c'est un moyen de le faire avec les utilitaires Shell dropdb & pg_ctl (ou pg_ctlcluster dans Debian et ses dérivés). Mais méthode @ filiprem est supérieur pour plusieurs raisons:

  • Il déconnecte uniquement les utilisateurs de la base de données en question.
  • Il n'a pas besoin de redémarrer l'ensemble du cluster.
  • Il empêche les reconnexions immédiates, endommageant éventuellement la commande dropdb.

Je cite man pg_ctlcluster:

Avec le --force option le mode "rapide" est utilisé qui annule toutes les transactions actives, déconnecte immédiatement les clients et s'arrête donc proprement. Si cela ne fonctionne pas, l'arrêt est à nouveau tenté en mode "immédiat", ce qui peut laisser le cluster dans un état incohérent et entraînera ainsi une exécution de la récupération au prochain démarrage. Si cela n'aide toujours pas, le processus du postmaster est interrompu. Quitte avec 0 en cas de succès, avec 2 si le serveur ne fonctionne pas et avec 1 dans les autres conditions d'échec. Ce mode ne doit être utilisé que lorsque la machine est sur le point d'être arrêtée.

pg_ctlcluster 9.1 main restart --force

ou

pg_ctl restart -D datadir -m fast

ou

pg_ctl restart -D datadir -m immediate

immédiatement suivi de:

dropdb mydb

Peut-être dans un script pour une succession immédiate.

7
Erwin Brandstetter

Si vous êtes sur quelque chose comme RDS où les connexions sans base de données sélectionnée vous placent dans la base de données que vous avez demandé à créer par défaut, vous pouvez faire cette variante pour contourner le fait d'être la dernière connexion ouverte.

 DROP DATABASE IF EXISTS temporary_db_that_shouldnt_exist; 

 CREATE DATABASE temporary_db_that_shouldnt_exist with OWNER your_user; 

 \connect temporary_db_that_shouldnt_exist 
 SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'the_db_you_want_removed'; 


 DROP DATABASE IF EXISTS the_db_you_want_removed; 
 -- 
 -- Name: the_db_you_want_removed; Type: DATABASE; Schema: -; Owner: your_user 
 -- 

 CREATE DATABASE savings_champion WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8'; 


 ALTER DATABASE the_db_you_want_removed OWNER TO your_user; 

 \connect the_db_you_want_removed 

 DROP DATABASE IF EXISTS temporary_db_that_shouldnt_exist;
0
Jharwood