同一主机跨数据库复制表数据遇java.lang.OutOfMemoryError问题求助
解决大数据集复制时的OutOfMemoryError问题
这个问题我之前踩过好几次,核心原因就是默认情况下JDBC的ResultSet会把查询到的所有数据一次性加载到JVM内存里,数据量一大直接就爆内存了。给你几个亲测有效的解决思路:
1. 开启流式结果集,分批读取源数据
大多数JDBC驱动支持流式读取结果集,也就是每次只从数据库拉取一小部分数据,而不是一次性加载全部。你需要修改PreparedStatement的创建方式,设置合适的结果集类型和fetch size:
// 创建流式读取的PreparedStatement PreparedStatement prepSqlStatmOnSrcDB = dbConnOnSrcDB.prepareStatement( sqlQueryOnSrcDB, ResultSet.TYPE_FORWARD_ONLY, // 只允许向前遍历结果集,减少内存开销 ResultSet.CONCUR_READ_ONLY // 结果集只读,避免额外的内存占用 ); // 对MySQL驱动来说,设置fetchSize为Integer.MIN_VALUE会开启流式模式 prepSqlStatmOnSrcDB.setFetchSize(Integer.MIN_VALUE);
注意:不同数据库驱动的流式配置可能有差异,比如Oracle驱动不需要设
Integer.MIN_VALUE,直接设置一个合理的数值(比如1000)即可实现分批读取。
2. 批量插入目标数据库
即使流式读取了源数据,如果每次插入一条记录到目标库,不仅效率低,还可能因为频繁的数据库交互导致额外内存消耗。建议用批量插入的方式,攒够一定数量的记录再一次性提交:
// 先关闭目标库的自动提交,批量插入后手动提交,减少事务开销 dbConnOnDestDB.setAutoCommit(false); int batchSize = 1000; // 可根据实际情况调整,比如500或2000 int recordCount = 0; // 生成对应表的插入语句(注意要和表结构的列顺序匹配) String insertSql = "INSERT INTO " + tableNameOnDestDB + " (col1, col2, col3) VALUES (?, ?, ?)"; try (PreparedStatement insertStmt = dbConnOnDestDB.prepareStatement(insertSql)) { ResultSet rs = prepSqlStatmOnSrcDB.executeQuery(); while (rs.next()) { // 给插入语句设置参数,对应表的列顺序 insertStmt.setString(1, rs.getString("col1")); insertStmt.setInt(2, rs.getInt("col2")); insertStmt.setTimestamp(3, rs.getTimestamp("col3")); insertStmt.addBatch(); recordCount++; // 达到批量阈值时执行插入并提交 if (recordCount % batchSize == 0) { insertStmt.executeBatch(); dbConnOnDestDB.commit(); recordCount = 0; } } // 处理最后一批不足batchSize的记录 if (recordCount > 0) { insertStmt.executeBatch(); dbConnOnDestDB.commit(); } } catch (SQLException e) { dbConnOnDestDB.rollback(); // 出错时回滚所有未提交的批量操作 throw e; } finally { // 恢复自动提交(可选,根据你的连接管理策略调整) dbConnOnDestDB.setAutoCommit(true); }
3. 额外优化建议
- 调整JVM内存参数:如果流式和批量处理后还是偶尔OOM,可以适当调大JVM堆内存,比如
-Xmx4G(根据服务器配置调整),但这只是治标,核心还是要从数据流处理入手。 - 使用数据库原生工具:如果不需要Java代码的灵活性,优先用数据库自带的复制工具,比如MySQL的
mysqldump、PostgreSQL的pg_dump,这些工具是专门为大数据量复制优化的,效率比Java代码高得多。 - 严格清理资源:确保所有
ResultSet、PreparedStatement都通过try-with-resources自动关闭,避免资源泄露导致的内存占用。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

