如何安全地终止已超时的查询

时间:2019-04-04 17:18:33

标签: java postgresql jdbc timeout

我正在使用PostgreSQL JDBC,并且具有一些选择和插入查询的连接。一些查询需要一些时间,因此我添加了超时。问题是超时会关闭连接,但是查询仍在数据库服务器中执行,并且会创建锁。

针对该问题的简化代码(实际代码复杂得多,但并不重要):

    PGPoolingDataSource source = new PGPoolingDataSource();
    source.setUrl(url);
    source.setUser(user);
    source.setPassword(password);
    source.setMaxConnections(10);
    source.setSocketTimeout(5); // Timeout of 5 seconds

    // Insert a row data, and make timeout
    Connection con = source.getConnection();
    con.setAutoCommit(false);
    try {
        Statement st2 = con.createStatement();
        st2.execute("insert into data.person values (4, 'a')");

        Statement st3 = con.createStatement();
        st3.executeQuery("select pg_sleep(200)"); // A query which takes a lot of time and causes a timeout
        con.commit();
        con.close();
    } catch (SQLException ex) {

        if (!con.isClosed()) {
            con.rollback();
            con.close();
        }
        ex.printStackTrace();
    }


    Connection con2 = source.getConnection();
    con2.setAutoCommit(false);
    try {
        Statement st2 = con2.createStatement();

        // This insert query is locked because the query before is still executed, and the rollback didn't happen yet, and the row with the id of 4 is not released
        st2.execute("insert into data.person values (4, 'b')");
        con2.commit();
        con2.close();
    } catch (SQLException ex) {

        if (!con2.isClosed()) {
            con2.rollback();
            con2.close();
        }
        ex.printStackTrace();
    }

({data.person是具有idname的表。)

超时将关闭连接,甚至没有到达行con.rollback();。我已经读到,当查询中发生异常时,它会在后台回滚,所以可以。

但是查询需要很多时间(几个小时),因此,大选择查询完成后将发生回滚。因此,我无法将行添加到data.person几个小时(第二次尝试插入时,我收到超时异常,因为它等待释放锁...)。

我已阅读到可以在PostgreSQL中使用函数pg_terminate_backend终止查询,因此可以第二次执行插入查询。

我的问题是:

1)它有多安全?

2)此解决方案有多常见?

3)是否有JDBC或PostgreSQL提供的更安全的解决方案?

1 个答案:

答案 0 :(得分:1)

如果您要中断查询并关闭数据库连接,

pg_terminate_backend将是安全且正确的过程。

还有pg_cancel_backend会中断查询,但保持连接打开状态。

这些功能要求您知道会话后端进程的进程ID,您可以使用pg_backend_pid函数获得该ID。

您必须在与原始数据库连接不同的数据库连接上运行这些语句!

另一种可能更简单的方法是设置statement_timeout。这可以在配置文件中设置,也可以为单个会话或事务设置。要将其设置为交易,请使用:

BEGIN;  -- in JDBC, use setAutoCommit(false)
SET LOCAL statement_timeout = 30000;  -- in milliseconds
SELECT /* your query */;
COMMIT;