java多线程分批入库核心是切分数据为小批次并并发执行:按语义或范围分片,单批100~1000条;线程池大小设为数据库连接池的70%~90%;每批次独立事务批量插入;避免共享状态,用atomicinteger或completablefuture汇总结果。

Java 中用多线程结合线程池实现并行分批入库,核心是:把大批量数据切分成多个小批次,每个批次交给一个独立任务提交到线程池执行,由各个线程并发调用数据库插入逻辑。关键在于控制并发数、避免连接冲突、保证事务边界清晰、防止 OOM。
合理划分批次 + 控制并发数
不要把 10 万条数据直接丢给 20 个线程各处理 5000 条——若每批都开启事务且未及时提交,可能压爆数据库连接或事务日志。建议:
- 按业务语义或主键范围切分(如按 user_id 取模、按时间区间、或简单 list.subList)
- 单批次大小建议 100~1000 条,具体看单条数据体积和数据库吞吐能力
- 线程池用 FixedThreadPool 或 ThreadPoolExecutor,核心线程数通常设为数据库连接池最大连接数的 70%~90%(例如 HikariCP maxPoolSize=20,线程池 coreSize=12~16)
- 拒绝策略推荐 CallerRunsPolicy,避免任务丢失,让提交线程自己执行溢出任务
每个任务内使用独立事务 + 批量插入
每个线程处理一个批次时,应在一个事务内完成该批次所有插入,而不是每条都 commit。Spring 环境下可这样写:
@Transactional(rollbackFor = Exception.class)
public void insertBatch(List<user> batch) {
userMapper.insertBatch(batch); // MyBatis 的 <foreach> 或 BatchExecutor
}</foreach></user>
非 Spring 场景需手动管理 Connection 和 PreparedStatement:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 从连接池获取 Connection,设置
setAutoCommit(false) - 用
PreparedStatement.addBatch()累加,再executeBatch() - 成功则
commit(),异常则rollback(),最后close()
避免共享资源竞争与状态错乱
多个线程并行执行时,注意以下常见陷阱:
- Mapper 实例是线程安全的(MyBatis SqlSession 是非线程安全的,但 Spring 管理的 Mapper 是代理,每次调用自动绑定新 SqlSession)
- 不要在任务中修改外部共享集合(如 static List),改用局部变量
- 如果需要汇总结果(如成功/失败计数),用 AtomicInteger 或 CompletableFuture.allOf() 收集返回值
- 避免在任务里直接操作同一个文件、缓存 key 或静态计数器,除非加锁或用线程安全类型
完整示例(基于 Spring Boot + MyBatis)
假设有一批 50000 条 User 对象要入库:
// 1. 切分批次
int batchSize = 500;
List<list>> batches = Lists.partition(users, batchSize);
// 2. 提交任务
ExecutorService pool = Executors.newFixedThreadPool(10);
List<future>> futures = new ArrayList();
for (List<user> batch : batches) {
futures.add(pool.submit(() -> {
try {
userMapper.insertBatch(batch); // 单批次事务内批量插入
return batch.size();
} catch (Exception e) {
log.error("batch insert failed", e);
return 0;
}
}));
}
// 3. 等待全部完成并统计
int totalSuccess = futures.stream()
.mapToInt(f -> {
try { return f.get(); }
catch (Exception e) { return 0; }
})
.sum();
pool.shutdown();
</user></future></list>
注意:实际项目中建议用 ThreadPoolExecutor 替代 Executors.newFixedThreadPool,便于监控队列长度、拒绝日志等。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










