PreparedStatement配合rewriteBatchedStatements=true是Java高效写入百万级数据的主流方案,需合理分批(1000–5000条/批)、禁用SQL拼接、并调优MySQL参数如innodb_flush_log_at_trx_commit和max_allowed_packet。

用 PreparedStatement 配合批处理 + JDBC 连接参数优化,是 Java 中安全又高效写入百万级数据的主流方案。关键不在“能不能”,而在“怎么配、怎么分、怎么控”——绕开单条执行、避免 SQL 拼接、压住内存、利用数据库底层机制。
必须开启 rewriteBatchedStatements=true
MySQL 的 JDBC 驱动默认把 batch 当作多条独立语句发过去,实际没真正批量。加这个参数后,驱动会把多个 INSERT 合并成一条带 VALUES (..),(..),(..) 的语句发送,网络往返和 SQL 解析开销大幅下降。
连接 URL 示例:
jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
- 不加该参数,即使调用了
addBatch()和executeBatch(),性能提升也极其有限(实测可能只快 2–3 倍) - Oracle 驱动默认开启,MySQL 必须显式配置
- 该参数仅对
PreparedStatement生效,对Statement无效
合理设置批大小,别一股脑全塞
一次提交 1000 条或 5000 条通常比 10 万条更稳——太大易 OOM 或触发 MySQL 单包限制(max_allowed_packet),太小则批处理收益不足。
- 推荐初始值:1000~5000 条/批(视单行数据大小动态调整)
- 每插入一批就调用一次
executeBatch(),并清空clearBatch() - 示例节选:
if ((i + 1) % 1000 == 0) {<br> ps.executeBatch();<br> ps.clearBatch();<br>}
用 PreparedStatement,别拼 SQL 字符串
既防 SQL 注入,又让数据库复用执行计划。所有字段都用 setXXX() 绑定,不拼接字符串、不插变量。
立即学习“Java免费学习笔记(深入)”;
错误示范:"INSERT INTO user VALUES ('" + name + "', " + age + ")" → 危险且无法批处理
正确写法:
String sql = "INSERT INTO user(name, age, email) VALUES (?, ?, ?)";<br>PreparedStatement ps = conn.prepareStatement(sql);<br>ps.setString(1, user.getName());<br>ps.setInt(2, user.getAge());<br>ps.setString(3, user.getEmail());<br>ps.addBatch();
- 预编译一次,后续只传参数,省去重复解析
- 自动类型转换与空值处理更安全
- 配合
rewriteBatchedStatements=true才能真正触发多值 INSERT 优化
配套做几项关键数据库调优
JDBC 层优化只是半程,MySQL 侧若还按默认配置,瓶颈很快卡在日志刷盘或缓冲区上。
-
innodb_flush_log_at_trx_commit = 2(测试/导入场景可用;生产如需强一致性设为 1) -
innodb_log_buffer_size = 16M(减少日志写磁盘频次) -
innodb_log_file_size = 128M(降低 checkpoint 频率) -
max_allowed_packet = 64M(避免大批次被截断)
这些参数需重启 MySQL 生效,建议在数据导入前临时调整,完成后按需恢复。



















