【问题标题】:Can I re-use a PreparedStatement if it hits a transient exception?如果 PreparedStatement 遇到暂时性异常,我可以重新使用它吗?
【发布时间】:2014-07-09 05:33:48
【问题描述】:

我有一段代码可以更新数据库中的记录。简化示例:

...
statement = connection.prepareStatement(
    "INSERT INTO thistable (name) VALUES (?)",
    PreparedStatement.RETURN_GENERATED_KEYS
);
statement.setString( 1, "Fred" );
statement.addBatch( );
statement.setString( 2, "Joe" );
statement.addBatch( );
statement.executeBatch( );
...

这是处理大量记录的某些代码的一部分,并且该代码正在运行大量线程以提高速度。这一切都很好,但是随着实时环境的负载增加,我注意到一些SQLTransientExceptions 被抛出。除了重试事务,我似乎无能为力。

我的问题是:即使语句失败,批次是否已被清除?我可以简单地重试executeBatch 行,还是需要重新创建整个批次?非批处理语句也一样吗?


简而言之,更一般地说,这是处理瞬态异常的好方法吗?

statement.setString( 1, "Fred" );
statement.addBatch( );
statement.setString( 2, "Joe" );
statement.addBatch( );
for( int attempt = 0; ; attempt ++ ) {
    try {
        statement.executeBatch( );
        break;
    } catch( SQLTransientException ex ) {
        if( attempt >= 3 ) {
            throw ex;
        }
        try {
            Thread.sleep( attempt * attempt * 100 );
        } catch( InterruptedException ex2 ) {
            throw ex;
        }
    }
}

【问题讨论】:

  • 我已经标记了这个问题 MySQL,因为这是我们目前正在使用的,但我希望了解标准行为,而不是特定于实现的行为,因为我们很快就会迁移到 Postgres .
  • 理论上 SQLTransientException 应该只在错误允许可以在没有“应用程序级功能的任何干预”的情况下重试的操作时抛出。
  • 您是否有任何 SQL 状态用于此异常 (getSQLState())?
  • @Mani,恐怕我不知道它的存在所以我没有记录它。
  • @user2574853的回答评论:虽然我贴的例子没有说明,但每批添加的10条左右的记录确实是一批;它们都与同一个项目相关,如果其中任何一个失败,则添加其他项目毫无意义。

标签: java mysql jdbc innodb


【解决方案1】:

由于API 没有提供太多关于当 SQLTransientException 除了下面发生时的行为的信息

 when the driver has determined that the timeout value that was specified by the 
 setQueryTimeout method has been exceeded and has at least attempted
 to cancel the currently running Statement

验证了 Mysql JDBC4 的实现。

根据我的观察,我可以看到如果超时,可能会发生SQLTimeoutException(即SQLTransientException)。

当超时时,它不仅清除了最后的语句,而且清除了整个 Batch(至少在 MYSQL 实现中)

// we timeout the entire batch, not individual statements

所以那部分很清楚。

并且根据 JDBC 规范

The statement’s batch is reset to empty once executeBatch returns

无论 executeBatch 抛出异常/成功,它都会清除 Batch。

特别是 Mysql Prepared Statement 在 executeBatch() 方法中的 finaly 块中清除批处理

finally {
        this.statementExecuting.set(false);
        clearBatch();
        }

因此您的逻辑将不起作用,因为您没有再次将语句添加到批处理中。

所以将语句添加回批处理并重新执行。您很有可能再次陷入超时。所以先找时间出去。如果可能的话,在执行日志中写一些关于原因/状态的信息。

【讨论】:

  • 手动清除(以防万一)然后重新填充它。很遗憾 - 我希望这可以隐藏在一个方法中。
  • 只是添加(对于未来的访问者)关于非批处理语句的相关问题,根据此文档,不需要重新填充值:docs.oracle.com/javase/7/docs/api/java/sql/…
【解决方案2】:

什么是异常消息?不确定是否由于mysql连接问题而失败。此外,我认为处理长请求并不好。您可以通过逐个运行多次运行它以实现您的目标。

【讨论】:

  • 在问题的 cmets 中回答了这个问题。为了将来参考,您不应发布 cmets/questions 作为答案。
猜你喜欢
  • 2013-02-23
  • 1970-01-01
  • 2019-11-20
  • 2021-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-31
  • 2011-08-05
相关资源
最近更新 更多