2012年1月9日,去客户现场解决性能问题。
背景:客户需要数据从一个数据库到另一个数据库转移。本来可以使用ETL工具解决,但是由于一些客观原因又不能使用ETL系统,因此项目组自己做了一个数据迁移功能。除了业务部分的数据组织外,其他功能非常简单,就是利用JDBC组织数据,然后批量提交。
问题:数据量在19万条,内存控制在1G左右,在8万条左右,内存溢出。监控JVM,发现Old区域和eden区域都满了,GC很吃力,效果不好。因此定位内存泄漏。
代码分析:
原有代码使用hibernate分页获取数据,每次5000条,然后进入子程序,创建PreparedStatement,批量组织sql,提交,之后释放PreparedStatement和connection。规范都可以,最后释放也在finally块中。
第一步怀疑,hibernate的级联查询有问题,关闭jdbc部分,只查询。JVM监控没有问题,19万很快结束。
第二步怀疑,其他部分影响,通过堆dump分析,发现DB2的JDBC驱动类比较多。
第三步分析jvm监控,发现系统分页每次5000条执行结束后,内存跳一大块,五六次就把内存占满,因此基本确定是JDBC部分的问题。
第四步,增加了PreparedStatement批量操作的清理功能,比如pstat.clearBatch();,发现用处不大,依然问题依旧。
第五步,分析发现19万分每次5000笔也是38次,38次connnection和PreparedStatement可能有问题。因此修改代码,将链接和PreparedStatement都提出来,在循环之外。然后每次批量执行完毕都执行清理操作(pstat.clearBatch())。内存稳定,没有增加,问题解决。
结论:PreparedStatement批量执行方式占用内存有可能非常大(跟批量数据量有关系),如果只是使用close,包括connection的close,并不能及时释放,哪怕是强制gc也不能释放。
解决的方法就是使用统一个PreparedStatement,那么假设它占用50M的空间,循环使用的情况下,只是覆盖没有新new一些地址,可能就是解决问题的思路。
不过上述结论没有经过全面测试和知识分析还存在一些疑问。
1,db2和oracle是否都这样?
2,怎么样才是PreparedStatement真正释放的方法,跟数据库连接池是否有关系。为什么我们原始代码不能及时gc掉呢?
3,数据迁移最佳模式是什么,我们做的是否是最佳的。我觉得只能是最简单的。
分享到:
相关推荐
当处理大数据时,常见的问题是内存溢出和性能瓶颈。为了克服这些问题,可以采取以下几种策略: 1. **使用批处理:** 将多条SQL语句打包成一个批处理操作,一次提交给数据库。 2. **合理使用PreparedStatement:** ...
10. **JDBC批处理API**: Hibernate底层使用JDBC的批处理API(Statement.addBatch()和Statement.executeBatch()),用户也可以直接使用JDBC批处理,绕过Hibernate的部分逻辑,获取更高的性能。 11. **性能优化注意...
- 注意处理大结果集时的内存管理,避免一次性加载大量数据导致内存溢出。 - 使用JDBC的批处理功能提高批量操作的效率。 6. **常见问题及解决方案** - 连接超时:检查网络状况,或者调整数据库的连接超时设置。 ...
批量过大可能导致内存溢出,过小则可能无法充分利用批处理的优势。 - **事务管理**:将批量插入操作包裹在事务中,可以确保数据的一致性。如果在批量操作中发生错误,整个事务可以回滚,避免了部分插入的数据不...
- **大结果集**:对于大量数据,考虑使用批处理或存储过程,避免一次性加载所有数据导致内存溢出。 4. **错误处理** - **捕获SQLException**:使用try-catch块捕获并处理SQLException,获取具体的错误信息。 - *...
ResultSet中的游标允许按需读取数据,避免一次性加载大量结果集导致内存溢出。批处理功能允许一次性发送多条SQL语句,提高执行效率。 10. 错误处理和关闭资源: 使用try-with-resources语句可以自动关闭数据库...
2. **迭代器模式**:提供一个迭代器接口,使得遍历结果集更加灵活且节省内存,避免一次性加载所有数据导致内存溢出。 3. **类型转换**:根据需要自动将数据库中的值转换为适当的Java类型,例如将字符串转换为日期、...
- 使用ResultSet的滚动和定位功能,可以更高效地遍历大量数据,避免一次性加载全部记录导致内存溢出。 - ResultSet的关闭非常重要,及时关闭可以释放数据库资源,防止内存泄露。 5. **事务管理(Transaction ...
1. **读取Excel**:使用Apache POI等库读取Excel文件,可以逐行或按需加载数据,避免一次性加载整个文件导致内存溢出。 2. **预处理数据**:在读取数据后,可能需要进行数据清洗、格式转换等预处理步骤,以符合...
如果需要处理大量数据,应考虑使用迭代器或流,以避免内存溢出。 此外,JDBC还提供了事务管理功能,通过Connection对象的`setAutoCommit()`和`commit()`方法可以控制事务的提交和回滚。对于性能优化,可以使用...
- **内存管理**:合理规划内存使用,确保不会因为内存溢出而导致程序崩溃。 - **测试验证**:在生产环境中部署前,一定要充分测试批量处理逻辑,确保其稳定性和性能表现。 #### 五、总结 批量处理是提高Hibernate...
- 在处理大量数据时,要注意内存管理,避免因结果集过大导致的内存溢出问题。 - 考虑到性能优化,适当使用连接池来复用数据库连接。 总的来说,`sqljdbc.jar`是Java开发者与SQL Server数据库之间的重要桥梁,它为...
传统的Hibernate数据插入方式往往采用单次插入单个实体对象的方式,这在处理小规模数据时效率尚可,但在处理成千上万乃至百万级别的数据时,则显得力不从心,甚至可能导致内存溢出等问题。解决这一问题的关键在于...
`batch_size`设置的是JDBC批处理的大小,即在提交事务前,Hibernate会积累多少条SQL语句。增大这个值可以减少与数据库的交互次数,从而提高性能。 在处理批量插入时,避免内存溢出的关键在于合理控制缓存。上述代码...
4. **结果集管理**:正确处理ResultSet,尤其是在大数据量返回时,避免一次性加载所有数据导致内存溢出。使用迭代器或分页查询来控制数据的读取。 5. **事务管理**:对于涉及多条数据库操作的任务,如学生信息的...
4. **设置合适的批处理大小**:避免一次性处理太多数据导致内存溢出。 5. **配置连接池**:根据应用需求调整连接池大小,避免空闲连接过多或连接不足。 6. **处理异常**:捕获并适当地处理SQL异常,确保程序健壮性。...
- 使用`LIMIT`子句限制返回的数据量,避免一次性加载大量数据导致内存溢出。如果可能,使用游标(Cursor)逐行处理结果。 7. **使用连接池的健康检查和超时设置**: - 配置合适的连接超时和空闲时间,确保连接池...
在IT行业中,处理大量数据是常见的任务之一,尤其是在数据分析、报表生成或数据迁移等领域。本教程将详述如何使用Apache POI库读取Excel数据,并通过Java的JDBC接口批量插入到MySQL数据库中。Apache POI是Java平台上...
在上述例子中,当尝试向数据库插入100,000条数据时,由于默认情况下Hibernate会将所有持久化对象保留在一级缓存中,随着数据量的增加,内存消耗也会迅速增长,最终导致内存溢出。因此,我们需要对一级缓存进行管理。...
过大可能导致内存溢出,过小则可能无法充分利用批量操作的优势。 在`JDBCBatchInsertTest.java`中,你可能会看到对这些批量插入方法的性能测试,通过计时和比较来确定哪种方法更适合特定的场景。 总结来说,选择...