数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案:
示例:
// 假设已有数据列表 dataList 和 PreparedStatement pstmt
int batchSize = 1000; // 每批处理量
int totalSize = dataList.size();for (int i = 0; i < totalSize; i++) {Data data = dataList.get(i);// 设置参数pstmt.setInt(1, data.getId());pstmt.setString(2, data.getName());// ...设置其他参数// 添加到批处理pstmt.addBatch();// 达到批次大小时执行并提交if ((i + 1) % batchSize == 0 || i == totalSize - 1) {pstmt.executeBatch();connection.commit();System.out.println("已提交一批,共 " + ((i % batchSize) + 1) + " 条");}
}
以上的代码逻辑是,每次都以1000条数据为一批进行入库,这样写的好处有以下几点:
- 降低内存占用:避免一次性加载所有数据导致内存溢出
- 减少GC压力:小批次处理产生的临时对象更少,减轻垃圾回收负担
- 适合流式处理:可以边读取边处理,无需全量数据驻留内存
- 减少数据库压力:避免大事务锁表时间过长
- 利用批处理API:数据库的批量操作通常比单条插入效率高
- 并行处理可能:不同批次可以并行执行(需考虑线程安全和资源竞争)
- 错误隔离:某批次失败不影响其他批次,可针对性重试
- 事务控制灵活:可以每批单独提交,避免大事务回滚代价高
- 断点续传:记录已处理批次,程序中断后可从中断点继续
- 避免超时:防止单次操作执行时间过长导致超时
- 负载均衡:将压力均匀分散到不同时间点
- 背压控制:根据系统处理能力动态调整批次大小
分批入库的实际项目问题:
而我在今天的工作中,由于是一个信创项目,需要由原来的Oracle数据库转为OpenGauess是数据库,由于OpenGauess数据库的性能远不如Oracle,所以在批次入库的时候,一直报超时和内存溢出的问题,还有一些类型不匹配的问题,开始我以为是新库的字段类型可能和之前的不一样,因为已经是使用分批入库了,所以就尝试了一下,先存一条数据,看看能不能存进去,测试之后发现可以存进去。这时问题就会出现了,一条数据库没问题传入进去,说明逻辑没有问题,难道是因为其中某一条数据有问题?于是我就把批次由1000改成了100,计划一百条数据为一个区间去找问题,结果运行完之后发现所有的数据都可以存进去!
于是就开始分析原因,最后发现确实是因为数据库性能的问题,导致批次过大。最终批次:
int batchSize = 100; // 每批处理量
....
//其余逻辑....
总结一下分批的典型应用场景:
- 大数据量ETL(数据抽取、转换、加载)
- 报表生成
- 数据迁移
- 批量消息处理
- 文件导入导出
分批处理是平衡性能、资源消耗和可靠性的重要技术手段,特别适合处理海量数据场景。