问题描述

  1. 我开发的网站加了个新功能:需要在线上处理表数据的批量合并和更新,昨天下午发布上线,执行该功能后,服务器的load突然增高,变化曲线异常,SA教育了我一番,让我尽快处理,将CPU负载降低。

  2. 工作所需,我经常要写些程序批量处理数据,每次执行几十万数据处理的时候,我机子的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。

(转自:http://www.verydemo.com/demo_c146_i31256.html)

Hibernate批处理操作优化 (批量插入、更新与删除)的更多相关文章

  1. .net使用SqlBulkCopy类操作DataTable批量插入数据库数据,然后分页查询坑

    在使用SqlBulkCopy类操作DataTable批量插入数据,这种操作插入数据的效率很高,就会导致每一条数据在保存的时间基本一样,在我们分页查询添加的数据是,使用数据的添加时间来排序就会出现每页的 ...

  2. SQL.Cookbook 读书笔记4 插入更新和删除

    第四章 插入更新和删除 4.1 插入数据 ,'PROGRA','NEW YOURK'); 4.2 从一个表向另一个表中复制 insert into dept_east(deptno,dname,loc ...

  3. Java批量插入更新操作

    以前总是说批量插入和更新的效率比非批量的要高,但是一直没有使用过批量处理数据的功能,现在由于项目中需要处理的数据量比较大,所以使用了批量处理的功能,java代码如下: 1.java实现批量插入数据: ...

  4. mapper mysl实现批量插入 更新

    1.批量插入 <insert id="insertConfirm" parameterType="java.util.List"> insert i ...

  5. MyBatis操作Oracle批量插入 ORA-00933: SQL 命令未正确结束

    最近在使用MyBatis操作Oracle数据库的时候,进行批量插入数据,思路是封装一个List集合通过Myabtis 的foreach标签进行循环插入,可是搬照Mysql的批量插入会产生 异常 ### ...

  6. Mysql优化批量插入数据

    最近为了测试项目,需要在Mysql中插入百万级测试数据,于是用到了批量插入,自己写了个简单的Spring Cloud项目. 开始时执行效率很慢,大概100条/秒,批次的大小也试过1000,2000,5 ...

  7. mysql批量插入更新操作

    //添加关联赠品(确定) public function addGiveGoods($ids,$child,$parent_sku_no){ $license=new LicenseModel(); ...

  8. Mysql数据库实践操作之————批量插入数据(100万级别的数据)

    第一种方法:使用insert into 插入 从Redis每次获取100条数据,根据条件去插入到Mysql数据库中: 条件: 如果当前队列中的值大于1000条,则会自动的条用该方法,该方法每次获取从队 ...

  9. Set,Sorted Set相关命令操作,批量插入及管道,事务

    Set SADD key member [member ...] 向key指定的set集合添加成员,次集合是排重的,从2.4版本后才支持添加多个如果key不存在则创建key以及set集合返回当前操作成 ...

随机推荐

  1. Linux下crontab命令的用法

    cron来源于希腊单词chronos(意为“时间”),是linux系统下一个自动执行指定任务的程序.例如,你想在每晚睡觉期间创建某些文件或文件夹的备份,就可以用cron来自动执行. 服务的启动和停止 ...

  2. Eclipse 一直提示 loading descriptor for 的解决方法(转)

    启动eclipse之后,进行相关操作时,弹出界面,提示:loading descriptor for xxx 解决方法: 在Eclipse左侧的Project Explorer 最右上角有一个小钮,鼠 ...

  3. js 函数定义三种方式

    <p>Js 函数定义的三种方式:</p> <br> <p>方式一:function</p> <script type="te ...

  4. Android 阅读Tasks and Back Stack文章后的重点摘抄

    这篇文章是做android的必读篇目,要仔细阅读,原文连接http://developer.android.com/guide/components/tasks-and-back-stack.html ...

  5. HDU 1087 Super Jumping! Jumping! Jumping! 最大递增子序列

    Super Jumping! Jumping! Jumping! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 ...

  6. iphone越狱还原

    在Cydia 里安装Impactor 就行了 .在操作时需要全程联网: .请至少保证 % 的电量以防止在恢复过程出现断电的情况(建议将设备连接至电源): .设备将恢复至出厂状态,所有用户数据都将被清除 ...

  7. php 练习

    下拉样式 <select> <?php $a = array( array("n001 汉族"), array("n002 回族"), arr ...

  8. show processlist

    mysql> show processlist; #mysql服务器查看有那些主机连进来,并列出它们查什么库 +-----+------+-----------+------+--------- ...

  9. 【Python】【解决】UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 1: ordinal not in range(128)

    1.问题描述 今天在升级Ubuntu到14.04,使用命令行启动软件更新器,进行版本升级,结果开始升级就异常退出了,具体打印如下: $update-manager -d 正在检查新版 Ubuntu 使 ...

  10. etc目录名字的意思---挖Linux中的古老缩略语

    Unix已经有35年历史了.许多人认为它开始于中世纪,这个中世纪是相对于计算机技术的产生和发展来说的.在过去的时间里,Unix和它的子分支Linux收集有许多的历史和一些完全古老的语言.在这篇技巧文章 ...