You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

批量插入190万条数据到MySQL时触发GC Overhead/Java堆空间错误

解决百万级数据批量插入MySQL的GC及堆空间问题

兄弟,我之前也踩过一模一样的批量插入大数据量的坑!你现在的问题核心就是一次性把190万条数据全塞在内存里处理,直接把JVM堆内存撑爆,触发GC过载甚至堆溢出错误。咱们一步步来解决:

1. 分批次处理,别一次性扛所有数据

你现在用ArrayList<String>存所有190万条数据,这本身就占了巨量内存。改成每处理一小批就提交一次,比如每1万条执行一次批量插入,然后清空这批数据,让GC能及时回收内存。

给你改个示例代码:

int batchSize = 10000; // 这个值可以根据你机器性能调整,比如2万、5万都试试
int count = 0;

try (Connection conn = getYourConnection()) {
    conn.setAutoCommit(false); // 关闭自动提交,大幅提升插入性能
    String insertSql = "INSERT INTO Triples (s, p, o) VALUES (?, ?, ?)";
    
    try (PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
        for (String triple : triples) {
            String[] tripleParts = triple.split("\\s+");
            pstmt.setString(1, tripleParts[0]);
            pstmt.setString(2, tripleParts[1]);
            pstmt.setString(3, tripleParts[2]);
            
            pstmt.addBatch();
            count++;
            
            // 达到批次大小就执行插入
            if (count % batchSize == 0) {
                pstmt.executeBatch();
                conn.commit();
                pstmt.clearBatch(); // 清空批次,释放内存
            }
        }
        // 处理最后一批不足batchSize的数据
        if (count % batchSize != 0) {
            pstmt.executeBatch();
            conn.commit();
        }
    }
} catch (SQLException e) {
    e.printStackTrace();
    // 别忘了回滚异常时的事务
    if (conn != null) {
        try {
            conn.rollback();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }
}

2. 给JVM堆内存加个“扩容包”

如果你的服务器有足够内存,调整JVM启动参数,给堆多分配点空间,比如:

-Xms4g -Xmx8g

-Xms是初始堆大小,-Xmx是最大堆大小。比如你服务器是16G内存,设-Xmx12g都没问题,但别超过物理内存的70%,避免触发系统swap拖慢性能。

3. 从根源减少内存占用:流式加载数据

如果你的数据是从文件或其他地方读来的,别一次性把所有数据都加载到ArrayList里!改成流式读取,比如用BufferedReader逐行读,读一行就加到批次里,这样内存里永远只有当前批次的数据,根本不会有百万级数据占内存的问题。

4. 数据库端也要“搭把手”

  • 关闭自动提交:上面代码里的conn.setAutoCommit(false)必须加,每次提交都会写redo log,批量提交能减少N倍的IO次数。
  • 调整InnoDB参数:比如把innodb_buffer_pool_size设为服务器内存的50%,innodb_log_file_size调大到2G左右(减少日志切换次数);如果可以接受少量数据丢失的风险,把innodb_flush_log_at_trx_commit设为2,写入性能会飙升(生产环境要严格ACID就保持1)。

5. 小细节优化:减少字符串拆分开销

split("\\s+")在百万级循环里也是不小的开销,如果你数据格式固定是空格分隔,可以换成StringTokenizer来拆分,性能会好一点:

StringTokenizer tokenizer = new StringTokenizer(triple);
String s = tokenizer.nextToken();
String p = tokenizer.nextToken();
String o = tokenizer.nextToken();

我用这套方法处理过300万条数据,全程没出过GC或堆溢出问题,你可以试试!

内容的提问来源于stack exchange,提问作者Prathamesh dhanawade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:31:44