Hibernate性能优化之前奏

Hibernate能极大地减少DAO层开发的代码量,减轻了程序员的负担。但是,除了save(),delete(),update(),query.list(), 要想精通Hibernate也是蛮难滴。如果我们还不是Hibernate高级玩家,那么,由我们写出来的代码极有可能因为大数据量、大并发量而造成内存的溢出以及数据库的崩溃!下面我就自己在项目开发中遇到的问题,浅谈一下Hibernate的性能优化问题。首先我要申明:我不是高玩,所以也只能浅谈,目的在于抛砖引玉,大家共同交流。

1,get()与load()的区别:
1.1,Hibernate一级缓存 我们知道,Hibernate为了减少数据库的访问频率,在内存中维护着一个事务级缓存,也叫一级缓存,凡是被Hibernate操作过的数据都会被纳入到一级缓存中去。这是一个什么样的数据结构呢?实际上就是一个Map,名字叫EntityByKey,以键值对的形式保存操作过的对象,其中,对象的id为键,对象本身为值。

1.2,get() Hibernate执行get方法,首先会去缓存中查找,如果在缓存中存在该对象,则直接返回该对象;如果没有,Hibernate则会去数据库中查找,如果数据库中也没有,则返回null。

1.3,load() Hibernate首先还是会去缓存中查找(跟get方法一样),如果缓存中没有,Hibernate会“假定”该对象在数据库中是一定存在的! 所以Hibernate不去查库,而是给我们返回了一个代理来“欺骗”我们,这个代理是Hibernate利用CGLIB技术在程序运行过程中通过改变字节码的方式来动态地生成目标对象的一个子类,它只加载了id属性,其他属性都没有初始化。当我们需要访问该对象的其他属性时,会触发Hibernate去查询数据库,如果没有该对象,则报异常:ObjectNotFoundException,因为Hibernate之前骗了人家,说该对象一定存在,现在却查不到,露馅了,所以报了异常。

1.4,懂了两者的区别之后,在实际应用中就要加以甄别。Eg:Person和Addressr一对一的关系,现在我要插入person之前需要设置private Address address属性,已知address的id,你可以get(Address.class,id)得到Address对象,然后person.setAddress(address)……,问题来了,设置一个属性就需要select访问一次数据库(假定缓存中没有数据),如果Person的关系属性很多,那insert之前就会有许多select执行,显然不行!person.setAddress(address)仅仅需要address的id属性,不必去查数据库加载address的其他属性,所以这时load的优势就体现了:既减轻了内存的开销,又避免了不必要的数据库访问。

2,list()与lierate()的区别:
这两个方法都是对hql语句的执行,但是何时用list,何时用iterate?
2.1,list():Hibernate直接去数据库把符合条件的对象查出来,无法利用缓存。
2.2,Iterate():Hibernate首先从数据库把符合条件的对象的id查出来,然后根据id依次查找。这样做可以充分地利用Hibernate的一级缓存。
所以,当你确定Hibernate缓存中已经存在你想要的数据时(这时,iterate的命中率比较高),可以用iterate()来减轻数据库的负载以及内存的开销。当第一次大规模查询数据时,适合list(),因为这时,缓存中没有数据。如果第一次就用iterate(),会造成最差效果:n+1次查询!

3,Hibernate一级缓存的管理
Hibernate的一级缓存处理得当,可以极大地方便我们对数据的访问,但是,它同时也是个双刃剑,首当其冲的就是内存,如果缓存中的数据量过大,极容易造成MemoryOut。所以有时我们要通过手动干预来管理Hibernate的一级缓存。

3.1,控制事务的大小,因为一级缓存是与事务绑定的,事务结束,缓存也就自动清空了。所以,事务不宜过大。

3.2,操作过大量数据后,如果担心因此导致MemoryOut,你可以及时通过session.clear()来清空缓存中的所有数据,或session.evict(Object)从缓存中清除一个对象。

3.3,对数据进行大规模DML操作时,合理控制bath-size的大小。因为Hibernate有一个特性:sql的延缓写入。意思是,当你update(Object)时,Hibernate不会马上更新数据库,而是把sql命令纳入到缓存中,等事务提交的时候一次性执行缓存中的sql。这样做可以利用批处理提高sql的执行效率,以及进行脏检,避免不必要的sql操作。所以我们要控制缓存中sql的写入时机:

for(int i=0;i<1000;i++){
       User u=list.get(i);
       u.setName(“zhangsan”);
       session.update(u);
       //设置bath-size为20
       if(i%20= =0){
              session.flush();
}
}

关于Hibernate性能优化还有许多需要注意的地方,下次再讲!

你可能感兴趣的:(Hibernate性能优化之前奏)