java批量插入不宜盲目多线程,需规避连接池耗尽、事务冲突和jdbc限制;应按业务分片隔离数据、启用rewritebatchedstatements=true、合理配比线程数与连接池大小,并手动控制事务粒度。

Java 中批量插入数据时用多线程并发提交,核心不是“越多线程越快”,而是要避开数据库连接瓶颈、事务冲突和 JDBC 驱动限制。直接为每批数据起一个线程+独立 Connection 去 insert,反而容易触发连接池耗尽、死锁或主键冲突,性能可能更差。
先明确:不是所有场景都适合多线程批量插入
如果目标表有唯一索引、自增主键、外键约束,或数据库本身是单节点 MySQL(非读写分离架构),并发插入极易引发锁等待甚至死锁。此时单线程分批次 + useServerPrepStmts=true&rewriteBatchedStatements=true(MySQL)往往更快更稳。
真需要并发时,关键在“拆分隔离”
让不同线程操作互不重叠的数据子集,避免行锁/间隙锁争抢:
- 按业务字段分片:比如按 user_id % N 分成 N 组,每组由一个线程处理,插入前确保该组数据在数据库中无主键/唯一键重叠
- 用临时表中转:各线程先写入各自命名的临时表(如 tmp_batch_1, tmp_batch_2),再由单线程统一 INSERT INTO ... SELECT ... ON DUPLICATE KEY UPDATE
- 关闭自动提交 + 手动控制事务粒度:每个线程内,每 1000 条左右 commit 一次,避免长事务拖慢整体吞吐
JDBC 层必须开启批量优化
否则即使开了多线程,每条 executeUpdate() 还是单条发包:
- MySQL 连接串加:rewriteBatchedStatements=true(把 addBatch() 合并成 INSERT INTO ... VALUES (),(),())
- Oracle 用 addBatch() + executeBatch(),配合 setFetchSize() 和合适的 batch size(500~2000)
- 避免在循环里 new PreparedStatement —— 每个线程复用同一个 PreparedStatement 实例
线程池与连接池要合理配比
假设 HikariCP 最大连接数是 20,那就不要用 50 个线程;建议线程数 ≤ 连接池最大连接数 × 1.5,并启用 allowMultiQueries=true(MySQL)提升复用率:
- 用 Executors.newFixedThreadPool(N),N 通常设为 CPU 核数 + 磁盘 I/O 并发能力(如 8~16)
- 每个线程从连接池获取 Connection 后立即开始 batch 插入,用完 close() 归还,不跨线程共享 Connection
- 监控连接池活跃数和等待时间,超过 100ms 就说明线程数过载了
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











