PostgreSQL错误:由于用户请求取消语句。

35

什么导致PostgreSQL出现这个错误?

org.postgresql.util.PSQLException: ERROR: canceling statement due to user request

我的软件版本:

PostgreSQL 9.1.6 on x86_64-redhat-linux-gnu, compiled by gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2), 64-bit"。

我的postgresql驱动程序是:postgresql-9.2-1000.jdbc4.jar

使用的java版本为:Java 1.7

提示:我的postgresql数据库安装在固态硬盘上,这个错误会随机发生,有时候也不会出现。


在PostgreSQL 9.2.1上(PostgreSQL 9.2.1在x86_64-unknown-linux-gnu上编译,由gcc(GCC)4.1.2 20080704(Red Hat 4.1.2-52)编译,为64位),使用相同的JDK和postgresql-9.2-1001.jdbc4.jar驱动程序时遇到了相同的问题。 数据库位于旋转驱动器上,语句取消会随机发生,应用程序不会取消任何SQL语句,并且语句超时设置为0(无限期)。 尚未找到原因/解决方案。 - reta
FYI:对于 postgresql-42.2.2 JDBC 驱动程序仍然适用。 - Andremoniy
我可能在postgresql-42.2.9 JDBC驱动程序上遇到了这个问题(但不确定,可能是其他原因)。我的情况很奇怪,当我删除按日期字段过滤的where和clause时,问题就消失了。 - combinatorist
1
升级到postgresql-42.2.18 JDBC驱动程序解决了我在AWS EMR 5.29上使用spark 2.4.4 scala 2.11作业时遇到的问题。 - combinatorist
4个回答

32
我们已经找到了这个问题的原因。最新的JDBC驱动程序9.2-100x中setQueryTimeout()的实现存在缺陷,这解释了其发生的原因。如果您手动打开/关闭连接,可能不会出现此问题,但是如果使用连接池并将autocommit设置为false,则很容易出现此问题。在这种情况下,应该使用非零值来调用setQueryTimeout()(例如,使用Spring框架的@Transactional(timeout = xxx)注释)。
事实证明,每当查询执行期间引发SQL异常时,取消计时器都没有被取消并保持活动状态(这就是它的实现方式)。由于连接池的作用,连接背后没有被关闭,而是返回到池中。稍后,当取消计时器触发时,它会随机取消当前与此计时器创建的连接相关联的查询。此时,它是完全不同的查询,这解释了其随机性效果。
建议的解决方法是放弃使用setQueryTimeout()并改用PostgreSQL配置(statement_timeout)。它不能提供相同级别的灵活性,但至少始终有效。

在v.9.4.1205中修复了一个可能相关的问题,不知道是否有关:https://github.com/pgjdbc/pgjdbc/issues/412 - rogerdpack

8
假设jdbc jar文件中的竞争条件bug导致了上述错误。
解决方法1:定期刷新与数据库的连接
一种解决方法是定期关闭与数据库的连接并创建一个新的连接。在执行几千个sql语句后,只需关闭连接并重新创建即可。然后,由于某种原因,此错误不再被抛出。
解决方法2:打开日志记录
如果在设置驱动程序时在JDBC驱动程序级别打开日志记录,则在某些情况下可以消除竞争条件问题:
Class.forName("org.postgresql.Driver");
org.postgresql.Driver.setLogLevel(org.postgresql.Driver.DEBUG);

解决方案3,捕获异常并重新初始化连接

您还可以尝试捕获特定的异常,重新初始化连接,然后再次尝试查询。

解决方案4,等待postgresql jdbc jar发布新版本进行修复

我认为这个问题可能与我的固态硬盘速度有关。如果您遇到此错误,请在此处发布如何 consistently 重现它的方法,因为开发人员非常感兴趣解决此问题。


链接显示404错误。 - combinatorist

6

如果您在不使用事务的情况下遇到此错误

用户请求取消语句。该语句正在执行它被告知要做的事情。问题是,谁请求取消该语句?

查看代码中为执行SQL准备的每一行。您可能有某种方法适用于取消语句的陈述,如下所示:

statement = conn.createStatement();
conn.setAutoCommit(true);
statement.setQueryTimeout(25);
my_insert_statement.setString(1, "moobars");

my_insert_statement.executeUpdate();
statement.close();

在我的情况下,发生的情况是我将查询超时设置为25秒,当插入操作超过该时间时。它会通过“由于用户请求取消语句”异常。 如果您在使用事务时遇到此错误: 如果收到此异常,请仔细检查所有执行SQL事务的代码。
如果您有一个在事务中的查询,并且您忘记提交,然后您使用该连接做其他事情,其中您操作就像没有在事务中一样,那么可能会产生未定义的行为,从而产生此异常。
确保执行事务的所有代码都在完成后清理自己。确保事务开始,完成工作,执行更多工作,然后将事务回滚或提交,然后确保连接处于autocommit=true状态。
如果这是您的问题,则异常不会在您忘记清理之后抛出,而是在长时间未清理事务之后的某个地方发生,这使得跟踪此异常变得很困难。刷新连接(关闭并获取新连接)将清除它。

4
除了Eric的建议外,当以下情况发生时,您可以看到语句取消:
  • 管理员或另一个连接以相同用户身份登录,并使用 pg_cancel_backend 要求您的会话取消其当前语句
  • 管理员向运行您的语句的PostgreSQL后端发送信号
  • 管理员请求快速关机或重新启动PostgreSQL服务器
请检查cron作业或负载管理工具,这些工具可能会取消长时间运行的查询。

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