PostgreSQL-由于一些自动连接到数据库,无法删除数据库

307

每当我尝试删除数据库时,都会出现以下错误:

ERROR:  database "pilot" is being accessed by other users
DETAIL:  There is 1 other session using the database.

当我使用:

SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'TARGET_DB';

我从那个数据库中断了连接,但是如果我在此之后尝试删除数据库,某种方式有人会自动连接到该数据库并出现错误。可能是什么原因导致这样?除我之外没有人使用这个数据库。

25个回答

10

就是这么简单

sudo service postgresql restart

5

对于我们而言,在Postgres 12上使用pgadmin、pgbouncer和多个客户端应用程序是可行的。

REVOKE CONNECT ON DATABASE <mydbname> FROM public;
ALTER DATABASE <mydbname> allow_connections = off;
SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '<mydbname>';
DROP DATABASE <mydbname>;

1
你可能需要执行 GRANT pg_signal_backend TO <用户名>; 才能实现该操作。 - iMx

5

首先:

sudo systemctl restart postgresql

那么:

drop database DATABASE_NAME;

2
虽然这可能在事实上是正确的,但添加解释会大大改善答案。 - Andrew

5

REVOKE CONNECT 操作不会阻止数据库属主或超级用户进行连接。因此,如果你不想让任何人连接到此数据库,可以使用以下命令:

alter database pilot allow_connections = off;

然后使用:

SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'pilot';

1
谢谢...在我的情况下,仅撤销连接是不够的。 - volpato
# alter database dbname allow_connections = off; ERROR: cannot disallow connections for current database - Eugene Gr. Philippov
@Eugene Gr. Philippov,您不能更改已登录的数据库,因此您应该切换到其他数据库并重试。 - inferno

4
在我的情况下,我正在使用基于Postgres的AWS Redshift。 并且似乎没有其他对数据库的连接,但我仍然收到相同的错误。
ERROR:  database "XYZ" is being accessed by other users

在我的情况下,数据库集群似乎仍在对数据库进行一些处理,尽管没有其他外部/用户连接,但数据库仍在内部使用中。我通过运行以下命令找到了这个问题:

SELECT * FROM stv_sessions;

因此,我的解决方案是在我的代码中编写一个循环,查找包含我的数据库名称的行。(当然,这个循环不是无限的,并且是一个有睡眠时间的循环等)

SELECT * FROM stv_sessions where db_name = 'XYZ';
如果有行被找到,就逐一删除每个PID。
SELECT pg_terminate_backend(PUT_PID_HERE);
如果没有找到任何行,继续删除数据库。
DROP DATABASE XYZ;
注意:在我的情况下,我正在编写Java单元/系统测试,这可能被认为是可以接受的。但这对于生产代码来说是不可接受的。
以下是完整的Java黑客方法(忽略我的测试/实用程序类)。
  int i = 0;
  while (i < 10) {
    try {
      i++;
      logStandardOut("First try to delete session PIDs, before dropping the DB");
      String getSessionPIDs = String.format("SELECT stv_sessions.process, stv_sessions.* FROM stv_sessions where db_name = '%s'", dbNameToReset);
      ResultSet resultSet = databaseConnection.execQuery(getSessionPIDs);
      while (resultSet.next()) {
        int sessionPID = resultSet.getInt(1);
        logStandardOut("killPID: %s", sessionPID);
        String killSessionPID = String.format("select pg_terminate_backend(%s)", sessionPID);
        try {
          databaseConnection.execQuery(killSessionPID);
        } catch (DatabaseException dbEx) {
          //This is most commonly when a session PID is transient, where it ended between my query and kill lines
          logStandardOut("Ignore it, you did your best: %s, %s", dbEx.getMessage(), dbEx.getCause());
        }
      }

      //Drop the DB now
      String dropDbSQL = String.format("DROP DATABASE %s", dbNameToReset);
      logStandardOut(dropDbSQL);
      databaseConnection.execStatement(dropDbSQL);
      break;
    } catch (MissingDatabaseException ex) {
      //ignore, if the DB was not there (to be dropped)
      logStandardOut(ex.getMessage());
      break;
    } catch (Exception ex) {
      logStandardOut("Something went wrong, sleeping for a bit: %s, %s", ex.getMessage(), ex.getCause());
      sleepMilliSec(1000);
    }
  }

这应该是正确的答案,其他那些重新启动服务的人,为什么不重启你们的电脑呢? - Siwei

4

我认为后台有一些空闲的查询正在运行。

  1. 先尝试显示正在运行的查询
SELECT pid, age(clock_timestamp(), query_start), usename, query 
FROM pg_stat_activity 
WHERE query != '<IDLE>' AND query NOT ILIKE '%pg_stat_activity%' 
ORDER BY query_start desc;
  1. 终止空闲查询(检查它们是否正在引用相关数据库,或者您可以杀死所有这些查询或使用选择结果中的PID 杀死特定查询)

SELECT pg_terminate_backend(procpid);

注意:终止select查询不会产生任何负面影响。


2
如果您正在使用Docker运行PostgreSQL服务器,请重新启动容器。

1
如果您在IntelliJ中遇到此错误,请确保通过单击下面显示的按钮在所有窗口中关闭连接enter image description here

1
在我的情况下,即使使用了以下命令,我仍然继续收到错误 - 因为在执行后立即创建了另一个用户连接。 撤销公共数据库<db_name>的连接:REVOKE CONNECT ON DATABASE <db_name> FROM public; 对我有用的是使用inferno上面(也在下面)的解决方案来防止连接。 关闭数据库<db_name>的连接:ALTER DATABASE <db_name> allow_connections = off 这允许我终止进程而不会立即重新创建该进程。 SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'TARGET_DB' -- ← 将其更改为您的DB AND pid <> pg_backend_pid();

0

对我来说,我只需重新启动postgresql。

systemctl restart postgresql

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,