不能一次性addbatch一万个参数再executebatch,因易触发oom、gc压力陡增及数据库拒绝大包;应分批控制(500–2000条/批)、边累积边执行、及时clearbatch、配合setautocommit(false)和rewritebatchedstatements=true。

Java 中 JDBC 批处理参数过多时,不能一次性 addBatch 一万个参数再 executeBatch,否则容易触发内存溢出(OutOfMemoryError)、GC 压力陡增,甚至数据库端拒绝大批次请求。核心解决思路是:**边累积、边执行、及时清空、分段控制**。
设置合理的批大小(500–2000 条为宜)
单次 executeBatch 的条数不是越多越好。太小(如 10 条)起不到减少交互的作用;太大(如 10000 条)会显著增加 JVM 堆内存占用,且 MySQL 等数据库对单次批量语句长度有隐式限制(例如 packet_max_size)。实测和生产经验表明:
- 500–1000 条适用于大多数 OLTP 场景,兼顾性能与稳定性
- 1000–2000 条适合高配环境 + rewriteBatchedStatements=true 启用后
- 超过 5000 条需谨慎评估,建议拆成多个子批次并加日志监控
循环中按模取余实现自动分段提交
在 for 或增强 for 循环内,通过索引或计数器判断是否达到阈值,触发执行与清空。这是最常用也最稳妥的方式:
- 使用普通 for 循环:if (i % 1000 == 0 && i > 0) { pstmt.executeBatch(); pstmt.clearBatch(); }
- 使用计数器(适合 List> 遍历):int count = 0; for (Data d : dataList) { ...; if (++count % 1000 == 0) { pstmt.executeBatch(); pstmt.clearBatch(); } }
- 注意:最后一次循环后别忘了执行剩余 batch —— 在循环外补调一次 executeBatch()
配合事务控制,避免每批都 commit
即使分了 10 批,也不代表要 commit 10 次。频繁 commit 会导致 WAL 写入放大、锁持有时间碎片化、主从延迟加剧。正确做法是:
- conn.setAutoCommit(false) 开启手动事务
- 每批 executeBatch() 后不立即 commit,只等全部数据处理完再统一 conn.commit()
- 若中途异常,调用 conn.rollback() 回滚整个逻辑单元,保证原子性
- 特别提醒:不要在循环内写 conn.commit(),那是典型反模式
启用数据库特有优化参数(MySQL 必配)
仅靠 Java 层分批还不够。MySQL 默认会把 addBatch 的多条 INSERT 当作独立语句发送,性能接近逐条执行。必须在 JDBC URL 中启用:
- rewriteBatchedStatements=true:驱动将多条 INSERT 重写为 INSERT INTO t VALUES (?,?),(?,?),… 形式,大幅降低解析开销
- 该参数不兼容含分号的 SQL,也不能和 allowMultiQueries=true 共存
- 验证是否生效:开启 MySQL general_log,观察实际发送的语句是否合并
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











