转自:http://youngflying.com/2012/09/14/hibernate-batch-processing/
问题描述
-
我开发的网站加了个新功能:需要在线上处理表数据的批量合并和更新,昨天下午发布上线,执行该功能后,服务器的load突然增高,变化曲线异常,SA教育了我一番,让我尽快处理,将CPU负载降低。
-
工作所需,我经常要写些程序批量处理数据,每次执行几十万数据处理的时候,我机子的CPU都会飙高,而且数据处理速度会越来越慢。比如第一个1W条要5分钟,第二个1W条就要10分钟,要干其他事情的时候机子也卡的不行,只能等着处理完数据。
其实我一直认为是数据量太大,从来不认为是程序问题,所以一直没怎么关注过。这次问题浮上表面,所以要好好解决下!
产生原因
主要原因:Hibernate的一级缓存影响。
我们每次保存的东西都会保存在Session缓存中,这就是Hibernate的一级缓存,如果我们一直循环执行save等操作,缓存里东西会越来越多,速度也就越来越慢,服务器一直在循环处理,自然也会增加负载。
这本来就是Hibernate不擅长的地方,而且一级缓存不可以不用,如果我们要保存的数据量十分巨大,那么在程序中执行添加、更新方法时,Session对象自身开辟的一级缓存会不断消耗,直至OutOfMemoryError (内存溢出异常)。
这就需要我们管理好Hibernate的缓存,或者不使用Hibernate。
解决方案
批量插入优化
1、仍旧用Hibernate API来进行批处理,但在一定的量的时候,及时的清除缓存。
1)优化Hibernate,在配置文件中设置hibernate.jdbc.batch_size参数,来指定每次提交SQL的数量。 配置hibernate.jdbc.batch_size参数的原因就是尽量少读数据库,hibernate.jdbc.batch_size参数值越大,读数据库的次数越少,速度越快。
<!--设置hibernate.jdbc.batch_size参数-->
<hibernate-configuration>
<session-factory>
.........
<property name="hibernate.jdbc.batch_size">50</property>
.........
<session-factory>
<hibernate-configuration>
2)程序及时清除缓存,即每插入一定量的数据后及时把它们从内部缓存中清除掉,释放占用的内存。 Session实现了异步write-behind,它允许Hibernate显式地写操作的批处理。
示例代码:
// 每处理50条清空缓存
session.save(myObject);
if (i/50 == 0) {
session.flush();
session.clear();
}
// 在我的项目中写法如下:
if (i/50 == 0) {
this.getHibernateTemplate().flush();
this.getHibernateTemplate().clear();
}
2、通过JDBC API来做批量插入,绕过Hibernate API。这个方法性能上是最好的,也是最快的。
示例代码:
String insertSql = "insert into user(name,address) values(?,?)";
Session session = getHibernateTemplate().getSessionFactory().openSession();
Connection conn = session.connection();
PrepareStatement stmt = conn.prepareStatement(insertSql);
// 方式1:自动提交
conn.setAutoCommit(true);
for(int i = 0; i++; i<10000) {
stmt.setString(1, "testName");
stmt.setString(2, "testAddress");
stmt.execute();
}
// 方式2:批量提交
conn.setAutoCommit(false);
for(int i = 0; i++; i<10000) {
stmt.setString(1, "testName");
stmt.setString(2, "testAddress");
stmt.addBatch();
if (i % 100 == 0) {
stmt.executeBatch();
conn.commit();
}
}
stmt.executeBatch();
conn.commit();
// 关闭session
session.close();
附测试数据:
// 测试方法:循环插入10000条数据,拆分10页,每页1000条。
// 直接Hibernate的save()方法,不做任何处理。
page 0 process time : 5925
page 1 process time : 6722
page 2 process time : 8019
page 3 process time : 9456
page 4 process time : 10263
page 5 process time : 11511
page 6 process time : 12988
page 7 process time : 13969
page 8 process time : 15196
page 9 process time : 16820
// Hibernate的save()方法,但每1个清除缓存。
page 0 process time : 10257
page 1 process time : 10709
page 2 process time : 11223
page 3 process time : 10595
page 4 process time : 10990
page 5 process time : 10222
page 6 process time : 10453
page 7 process time : 10196
page 8 process time : 9645
page 9 process time : 10295
// Hibernate的save()方法,但每50个清除缓存。
page 0 process time : 5848
page 1 process time : 5480
page 2 process time : 5739
page 3 process time : 5960
page 4 process time : 6287
page 5 process time : 5947
page 6 process time : 7012
page 7 process time : 6235
page 8 process time : 6063
page 9 process time : 6055
// JDBC的auto commit 方式
page 0 process time : 840
page 1 process time : 800
page 2 process time : 800
page 3 process time : 847
page 4 process time : 806
page 5 process time : 829
page 6 process time : 1042
page 7 process time : 893
page 8 process time : 857
page 9 process time : 854
// JDBC的batch方式,每50个commit
page 0 process time : 827
page 1 process time : 801
page 2 process time : 918
page 3 process time : 828
page 4 process time : 856
page 5 process time : 831
page 6 process time : 815
page 7 process time : 842
page 8 process time : 817
page 9 process time : 937
经测试:
1)若直接使用Hibernate,处理同样数据的时间会递增,甚至成倍增加,而且在测试过程中CPU使用率一直在70%上下。
2)若在使用Hibernate中每save一次都清空缓存的话,虽然时间不会递增,但处理速度很慢。在本例中采用每50个清空一次缓存较为合适,实际应用视情况而定。 一定量的时候清空缓存,虽然速度上没有提升,但会比较稳定,不会随着时间陡增,而且测试中CPU使用率也维持在20%上下,可以挽救一点性能损失,使系统相对稳定。
3)若使用JDBC API,不论auto commit方式还是batch方式,相比Hibernate在性能上都有近10倍的提升。不过在数据量较大的时候,推荐使用batch方式。
批量更新与删除优化
Hibernate2中,对于批量更新/删除操作,都是先将符合要求的数据查出来,然后再做更新/删除操作。这样一来会占用大量内存,而且海量数据处理的时候性能很低。
而Hibernate3对批量更新/删除提供了支持,能够直接执行批量更新或批量删除语句,无需把被更新或删除的对象先加载到内存中,类似于JDBC的批量更新/删除操作。
不过对于循环处理数据更新和删除场景,建议还是使用JDBC,方法同上:批量插入的方法2。
相关推荐
12. **监控与调优**: 使用数据库日志、监控工具(如JProfiler)或Hibernate的统计信息来分析批处理的效果,以便进一步优化。 总结,Hibernate的批处理是大数据场景下提高性能的关键技术之一,需要根据具体业务需求...
概述:本文主要介绍了Hibernate+JDBC实现批量插入、更新及删除的方法,通过实例形式详细分析了Hibernate与JDBC针对数据库的批量操作相关实现技巧。 知识点一:Hibernate一级缓存 * Hibernate一级缓存对其容量没有...
批量删除操作与批量更新类似,也应避免逐条删除的方式。可以使用类似批量更新的策略,即通过HQL或原生SQL执行一次性的删除操作,或者利用数据库的存储过程来提高效率。 ### 结论 在Hibernate应用中处理批量更新和...
- **标识符生成器限制**:如果使用“identity”作为主键生成策略,则Hibernate无法在JDBC层进行批量插入操作。 - **二级缓存管理**:在进行批量操作时,建议关闭Hibernate的二级缓存,以避免缓存同步问题。这可以...
本文将深入探讨在Hibernate中处理批量更新和批量删除的策略,以及如何优化这些操作,以提高数据库操作的效率。 ### 批量更新的常规方法 在Hibernate中,最直观的批量更新方式是通过循环遍历查询结果集,并对每个...
批处理在插入大量数据或执行重复更新时尤其有用。 5. **批处理测试**:测试批处理操作时,我们关注的是批处理的正确性、性能和边界情况。例如,测试是否在达到批处理大小后正确地提交了事务,以及在处理空集合或...
为了避免这种情况的发生,在进行批量插入操作时,可以采用以下策略: 1. **分批提交**:将大批量的数据分割成小批次,每完成一批次的插入操作后立即提交事务。这样可以有效地避免内存溢出的问题。 2. **使用Session...
Hibernate 的批量抓取机制允许将多个INSERT 语句合并成一个批量插入语句,从而提高性能。在 Hibernate 配置文件中,可以设置 `hibernate.jdbc.batch_size` 参数来指定批量抓取的大小。 如何实现批量更新 在 ...
对于更新和删除操作,可以使用`scroll()`方法,这在Hibernate 2.1.6或更高版本中是支持的。`scroll()`方法返回一个`ScrollableResults`对象,允许我们以游标方式处理结果集,这在处理大量数据时非常有效。同样,我们...
然而,在处理大量数据的批量操作时,如批量插入、更新或删除,如果不采取适当的策略,可能会导致性能问题甚至出现内存溢出异常。针对这种情况,Hibernate提供了一些批量处理的解决方案。 批量插入是处理大量数据...
为了解决这一问题,Hibernate提供了对JDBC的支持,使得开发者能够利用JDBC的批量处理能力来优化批量删除操作。 #### 实现方式一:使用Hibernate API 原始的实现方式是直接通过Hibernate API来执行批量删除操作,...
批处理是 Hibernate 中一种提高性能的方法,特别是在进行大量数据的插入、更新或删除时尤为明显。 - **默认行为** - 默认情况下,Hibernate 每次执行一条 SQL 语句后都会自动提交事务。 - **批量处理** - 可以...
- 设置合适的批处理大小,批量插入或更新数据可以提高效率。 - 关闭不必要的日志输出,减少IO开销。 5. **事务管理**: - 合理划分事务边界,避免长时间持有事务,减少锁竞争,提高并发性能。 - 使用Spring等...
- Hibernate支持批处理操作,如批量插入、更新或删除。通过设置`batch_size`参数,可以控制一次批处理操作中的记录数量,减少数据库往返次数,提高批量操作的效率。 **3. 二级缓存使用:** - 合理配置并使用...
本文将详细介绍如何在使用JSP和Hibernate环境下进行高效的批量更新与删除操作。 ### 批量更新与批量删除 批量操作指的是在数据库层面一次性对大量数据进行更新或删除。与单条记录的更新或删除相比,批量操作能大幅...
Session的批处理功能可以优化数据库操作,提高性能。 Spring框架则作为整个应用的“胶水”,提供依赖注入(DI)和面向切面编程(AOP)等功能。在批量删除的源码中,Spring管理着所有的bean,包括Struts2的Action、...
3. **批处理**:批量插入和更新数据,利用Hibernate的批处理功能,提高数据库操作效率。 五、事务管理优化 1. **合理划分事务边界**:根据业务逻辑划分事务大小,避免过大事务导致的性能问题。 2. **事务隔离级别...
本文件主要探讨了如何使用Hibernate进行批量更新和批量删除处理,这些操作在处理大量数据时尤其重要,因为它们可以显著提高应用的性能。下面我们将深入解析这两个主题。 批量更新在Hibernate中通常涉及在一个事务中...
例如,使用`Session.flush()`和`Session.clear()`控制事务边界,或配置`hibernate.jdbc.batch_size`来批量插入、更新和删除数据。 7. **避免N+1查询问题**: 当遍历一个集合并访问其关联属性时,可能会触发多次...
本篇文章将详细探讨如何利用JSF与Hibernate相结合来实现批量删除功能,以及在CRUD(创建、读取、更新和删除)操作中的应用。 首先,我们需要理解JSF的工作原理。JSF是一个基于组件的MVC(Model-View-Controller)...