PreparedStatement批量插入性能最优批大小为1000–5000条,建议从2000开始压测;必须关闭自动提交并手动commit;MySQL需启用rewriteBatchedStatements=true;预编译语句须复用,避免循环中重复prepare。

用 PreparedStatement.addBatch() 配合 executeBatch() 批量插入,比单条 executeUpdate() 快数倍甚至数十倍——关键不在“用了批处理”,而在于怎么配、怎么调、怎么避坑。
一次 addBatch 多少条最合理?
不是越多越好,也不是越少越稳。主流数据库(MySQL、PostgreSQL、Oracle)在 1000–5000 条/批之间通常达到吞吐与内存的平衡点:
- 太小(如 10 条/批):网络往返和 JDBC 驱动开销占比高,没发挥批量优势
- 太大(如 50000 条/批):容易触发 JVM 内存压力、驱动缓冲区溢出,或数据库单次事务过大导致锁等待/回滚风险上升
- 建议从 2000 开始压测,结合你实际的字段数量、平均行大小、网络延迟和数据库配置微调
必须关闭自动提交并手动 commit
默认 autoCommit = true 会让每批 executeBatch() 都隐式提交,彻底失去事务控制和性能收益:
-
错:
conn.setAutoCommit(true); stmt.executeBatch();→ 实际是 N 次独立事务 -
对:
conn.setAutoCommit(false); stmt.executeBatch(); conn.commit(); - 大批次可拆成多个小事务(如每 5000 条 commit 一次),兼顾性能与崩溃恢复能力
别忘了设置 rewriteBatchedStatements=true(MySQL 专属加速)
MySQL JDBC 驱动默认把 batch 转成多条独立 INSERT,形同虚设。加这个参数后,驱动会重写为一条带多个 VALUES 的 INSERT 语句:
- JDBC URL 示例:
jdbc:mysql://host/db?rewriteBatchedStatements=true - 效果显著:同样 2000 条插入,耗时可能从 800ms 降到 120ms
- 注意:仅对 INSERT 有效;UPDATE/DELETE 不支持;需 MySQL 5.1.13+ 和 Connector/J 5.1.13+
预编译语句要复用,别在循环里反复 prepare
每次 conn.prepareStatement(sql) 都有解析、校验、生成执行计划成本:
-
错:
for (row : data) { PreparedStatement ps = conn.prepareStatement("INSERT..."); ps.setString(...); ps.addBatch(); } - 对:prepare 一次,循环中只 set 参数 + addBatch,最后 executeBatch
- 如果 SQL 结构不同(如字段动态变化),考虑用 MyBatis 的
<foreach></foreach>或拼接 VALUES 列表,而非反复 prepare

















