JDBC批量插入OutOfMemoryError

24

我编写了一个名为insert()的方法,其中我正在尝试使用JDBC Batch将50万条记录插入到MySQL数据库中:

public void insert(int nameListId, String[] names) {
    String sql = "INSERT INTO name_list_subscribers (name_list_id, name, date_added)" + 
        " VALUES (?, ?, NOW())";
    Connection conn = null;
    PreparedStatement ps = null;

    try {
        conn = getConnection();
        ps = conn.prepareStatement(sql);

        for (String s : names ) {
            ps.setInt(1, nameListId); 
            ps.setString(2, s);
            ps.addBatch();
        }

        ps.executeBatch();

    } catch (SQLException e) {
        throw new RuntimeException(e);
    } finally {
        closeDbResources(ps, null, conn);
    }
}

但是每当我尝试运行这个方法时,我都会遇到以下错误:

java.lang.OutOfMemoryError: Java heap space
    com.mysql.jdbc.ServerPreparedStatement$BatchedBindValues.<init>(ServerPreparedStatement.java:72)
    com.mysql.jdbc.ServerPreparedStatement.addBatch(ServerPreparedStatement.java:330)
    org.apache.commons.dbcp.DelegatingPreparedStatement.addBatch(DelegatingPreparedStatement.java:171)
如果我将ps.addBatch()替换为ps.executeUpdate()并删除ps.executeBatch(),那么它可以正常工作,尽管需要一些时间。如果在这种情况下使用Batch是合适的,请让我知道,如果是的话,为什么会出现OutOfMemoryError? 感谢!
2个回答

49
addBatchexecuteBatch提供了批量插入的机制,但您仍然需要自己执行批处理算法。
如果您只是将每个语句堆叠到同一个批处理中,那么您将会耗尽内存。您需要在每个n记录时执行/清除批处理。 n的值由您决定,JDBC无法为您做出决策。批量大小越大,速度就越快,但太大会导致内存饥饿,速度会变慢或失败。这取决于您有多少内存。
例如,从1000开始进行批处理,并尝试不同的值。
final int batchSize = 1000;
int count = 0;
for(String s : names ) {
   ps.setInt(1, nameListId); 
   ps.setString(2, s);
   ps.addBatch();

   if (++count % batchSize == 0) {
      ps.executeBatch();
      ps.clearBatch(); //not sure if this is necessary
   }
}
ps.executeBatch();   // flush the last few records.

如果出现问题,这个能在事务中工作吗?允许回滚所有已执行的批处理吗? - Zephyr
我知道有点晚了,但我尝试使用clearBatch(),似乎没有帮助。它仍然会导致OOM(内存溢出)。有更好的方法来解决这个问题吗? - Daredevil
MySQL JDBC驱动程序不需要使用ps.clearBatch()。在StatementImpl.executeBatchInternal()中的每个ps.executeBatch()内部调用它。 - v.ladynev

6

由于所有事务都存储在内存中,只有在调用executeBatch时才会将其发送到数据库,因此出现了内存不足的情况。

如果您不需要它具有原子性并希望获得更好的性能,则可以保留计数器,并在记录数达到n时调用executeBatch


n的值应该是多少? - craftsman
3
价值取决于你,你需要对你的应用程序进行基准测试,以获得你想要在内存和性能之间权衡时所需的最佳价值。 - DJ.
我认为它将在同一事务中是原子的。 - v.ladynev

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