如何安全地终止已超时的查询
How to safely kill a query which has timed out
我正在使用 PostgreSQL JDBC,并且我有一些 select 和插入查询的连接。有些查询需要一些时间,所以我添加了一个超时时间。问题是超时关闭了连接,但查询仍在数据库服务器中执行并创建了锁。
该问题的简化代码(实际代码复杂得多,但没关系):
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
是 table 与 id
和 name
。)
超时关闭连接,连con.rollback();
这一行都没到。我读过当查询发生异常时它会在后台回滚,所以没关系。
但是查询会花费很多时间(几个小时),因此回滚将在 select 大查询完成后发生。因此,我无法将行添加到 data.person
几个小时(我第二次尝试插入时,出现超时异常,因为它等待锁被释放...)。
我读到可以使用PostgreSQL中的pg_terminate_backend
函数终止查询,这样我就可以执行第二次插入查询了。
我的问题是:
1) 它的安全性如何?
2) 这个解决方案有多普遍?
3) JDBC 或 PostgreSQL 提供了更安全的解决方案吗?
如果您想中断查询并关闭数据库连接,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;
我正在使用 PostgreSQL JDBC,并且我有一些 select 和插入查询的连接。有些查询需要一些时间,所以我添加了一个超时时间。问题是超时关闭了连接,但查询仍在数据库服务器中执行并创建了锁。
该问题的简化代码(实际代码复杂得多,但没关系):
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
是 table 与 id
和 name
。)
超时关闭连接,连con.rollback();
这一行都没到。我读过当查询发生异常时它会在后台回滚,所以没关系。
但是查询会花费很多时间(几个小时),因此回滚将在 select 大查询完成后发生。因此,我无法将行添加到 data.person
几个小时(我第二次尝试插入时,出现超时异常,因为它等待锁被释放...)。
我读到可以使用PostgreSQL中的pg_terminate_backend
函数终止查询,这样我就可以执行第二次插入查询了。
我的问题是:
1) 它的安全性如何?
2) 这个解决方案有多普遍?
3) JDBC 或 PostgreSQL 提供了更安全的解决方案吗?
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;