【问题标题】:JDBC batch update skipping out on initial set of update statementsJDBC 批量更新在初始更新语句集上跳过
【发布时间】:2015-07-23 21:23:51
【问题描述】:

我们在 Java 代码中使用 JDBC 批量更新(语句 - void addBatch(String sql) 和 int[] executeBatch())。该作业应该在表中插入大约 27k 条记录,然后在后续批次中更新大约 18k 条记录。

当我们的作业在早上 6 点运行时,它丢失了几千条记录(我们从数据库审计日志中观察到了这一点)。我们可以从作业日志中看到,正在为所有 18k 记录生成更新语句。我们知道所有更新语句都按顺序添加到批处理中,但是,似乎只有批处理开头的记录丢失了。此外,它不是每天的固定数字 - 一天,它跳过前 4534 条更新语句,另一天它跳过前 8853 条记录,另一天,它跳过 5648 条记录。

我们最初认为这可能是一个线程问题,但由于被跳过的块并不总是包含相同数量的更新语句,因此我们放弃了这种思考过程。如果我们假设前几千个更新甚至在插入之前就发生了,那么这些更新至少应该显示在数据库审计日志中。然而,事实并非如此。

我们认为这是由于内存/堆问题造成的,因为在任何其他时间运行作业都会获取所有 18k 更新语句并且它们已成功执行。我们查看了来自 Oracle 数据库的审计日志,并注意到在早上 6 点运行期间从未在表上执行丢失的更新语句。在任何其他时间,所有更新语句都会显示在数据库审计日志中。

这项工作已经成功运行了将近 3 年,而这种行为是从几周前才开始的。我们试图查看服务器/环境的任何变化,但没有任何变化。

我们正试图查明发生这种情况的原因,特别是,如果有任何进程占用了过多的 JVM 堆,从而导致我们的更新语句被覆盖/不被执行。

数据库:Oracle 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Java:Java版本“1.6.0_51” Java(TM) SE 运行时环境 (build 1.6.0_51-b11) Java HotSpot(TM) 服务器虚拟机(build 20.51-b01,混合模式)

void main()
{
    DataBuffer dataBuffer;//assume that all the selected data to be updated is stored in this object
    List<String> TransformedList = transform(dataBuffer);
    int status = bulkDML(TransformedList);
}

public List<String> transform(DataBuffer i_SourceData) 
{
    //i_SourceData has all the data selected from 
    //the source table, that has to be updated
    List<Row> AllRows = i_SourceData.getAllRows();
    List<String> AllColumns = i_SourceData.getColumnNames();
    List<String> transformedList = new ArrayList<String>();
    for(Row row: AllRows)
    {
        int index = AllColumns.indexOf("unq_idntfr_col");
        String unq_idntfr_val = (String)row.getFieldValues().get(index);
        index = AllColumns.indexOf("col1");
        String val1 = (String)row.getFieldValues().get(index);
        String query = null;
        query =   "UPDATE TABLE SET col1 = " + val1 + " where unq_idntfr_col=" + unq_idntfr_val;//this query is not the issue either - it is parameterized in our code
        transformedList.add(query);
    }
    return transformedList;
}

public int bulkDML(List<String> i_QueryList)
{
    Connection connection = getConnection();
    Statement statement = getStatement(connection);
    try
    {
        connection.setAutoCommit(false);
        for (String Query: i_QueryList)
        {
            statement.addBatch(Query);
        }
        statement.executeBatch();   
        connection.commit();
    }
    //handle various exceptions and all of them return -1
    //not pertinent to the issue at hand
    catch(Exception e)
    {
            return -1;
    }
    CloseResources(connection, statement, null);
    return 0;
}

任何建议将不胜感激,谢谢。

【问题讨论】:

  • 你能显示一些代码吗?
  • 我不确定代码的哪些部分会有所帮助。我们使用循环遍历 SQL 返回的数据,准备更新语句,然后批量执行。
  • 将有助于重现错误的代码,MCVE 将帮助我们。
  • 我尝试添加与手头问题相关的代码片段。但是,正如我之前所说,这是一项自过去 3 年以来一直运行良好的工作,并且当它再次/在不同时间执行时,该工作也运行良好 - 所以,我想相信它不是工作本身的问题,而是环境的一些变化。
  • 这段代码很容易受到 SQL 注入攻击,并且只是 "-- 在一个值中可能会使您的查询的其余部分根本不执行,而不是考虑造成更大的伤害。

标签: java jdbc oracle11g heap-memory batch-updates


【解决方案1】:

如果您想对同一个表执行多个更新,那么我建议修改您的查询以使用绑定和 PreparedStatement,因为这确实是使用 Oracle 数据库进行真正 DML 批处理的唯一方法。例如,您的查询将变为:

UPDATE TABLE SET col1=? WHERE unq_idntfr_col=?

然后使用具有相同 PreparedStatement 的 JDBC 批处理。此更改将需要您重新访问您的 bulkDML 方法,以使其将绑定值作为参数而不是 SQL。

JDBC 伪代码如下所示:

     PreparedStatement pstmt = connection.prepareCall("UPDATE TABLE SET col1=? WHERE unq_idntfr_col=?");
     pstmt.setXXX(1, x);
     pstmt.setYYY(2, y);
     pstmt.addBatch();
     pstmt.setXXX(1, x);
     pstmt.setYYY(2, y);
     pstmt.addBatch();
     pstmt.setXXX(1, x);
     pstmt.setYYY(2, y);
     pstmt.addBatch();
     pstmt.executeBatch();

【讨论】:

  • 让,感谢您的回复。但是,此代码直到最近才有效,大约 3 年没有任何问题。我们实际上想要了解的是我们应该关注哪些环境变量/设置来解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多