每当我尝试删除数据库,我得到以下错误:

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';

我终止了来自该数据库的连接,但如果我试图在此之后删除数据库,则有人以某种方式自动连接到该数据库并给出此错误。那是什么? 除了我,没人用这个数据库。


当前回答

在我的情况下,我使用AWS红移(基于Postgres)。 它似乎没有其他连接到DB,但我得到这个相同的错误。

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

其他回答

只需检查连接是什么,它来自哪里。你可以看到这些:

SELECT * FROM pg_stat_activity WHERE datname = 'TARGET_DB';

也许这是你们之间的联系?

你可以阻止未来连接:

REVOKE CONNECT ON DATABASE thedb FROM public;

(可能还有其他用户/角色;查看psql中的\l+

然后你可以终止所有到这个db的连接,除了你自己的:

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

在旧版本中,pid被称为procpid,所以你必须处理这个问题。

既然您已经撤销了CONNECT权限,那么试图自动连接的任何程序都不能再这样做了。

现在可以删除DB了。

如果您使用超级用户连接进行正常操作,这将不起作用,但如果您正在这样做,则需要首先解决这个问题。


删除数据库后,如果再次创建数据库,可以执行以下命令恢复访问

GRANT CONNECT ON DATABASE thedb TO public;

您需要确定的是使用DB的服务没有运行。

遇到同样的问题,运行一些Java应用程序,上面的选项都不起作用,甚至不能重新启动。

使用DB运行ps aux终止主服务。

kill -9 PID 或者如果应用程序作为服务运行,请确保为您的操作系统运行service stop cmd。

在此之后,删除表的默认方式将完美地工作。

在我的例子中是问题

如果对机器上的其他服务没有潜在影响,只需重启postgresql服务

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

systemctl restart postgresql