您的位置:首页 > 其它

hibernate的一些优化策略

2014-05-04 20:43 162 查看
Robbin总结的Hibernate性能优化要点(一):

1.尽量使用many-to-one,避免使用单项one-to-many

2.灵活使用单向one-to-many

3.不用一对一,使用多对一代替一对一

4.配置对象缓存,不使用集合缓存

5.一对多使用Bag 多对一使用Set

6.继承使用显示多态  HQL:from object  polymorphism="exlicit" 避免查处所有对象

7.消除大表,使用二级缓存

    对于上面这些,Robbin进行了详细的讲解。

    ==========================

    one-to-many:

     使用inverse=false(default),对象的关联关系是由parent对象来维护的

     而inverse=true的情况下,一般用户双向多对多关联,由子对象维护关联关系,增加子对象的时候需要显示:child.setParent(child)

     为了提高性能,应该尽量使用双向one-to-many inverse=true,在MVC结构中的DAO接口中应该直接用Session持久化对象,避免通过关联关系(这句话有点不理解),而在单项关系中正确使用二级缓存,则可以大幅提高以查询为主的应用。

     多对一性能问题比较少,但是要避免经典N+1问题。

     通过主键进行关联,相当于大表拆分小表。(这个是区分面向对象设计和面向过程设计的一个关键点)

     ===============================

     list、bag、set的正确运用

     one-to-many:

     A、使用list 需要维护Index Column字段,不能被用于双向关联,而且必须使用inverse=false,需要谨慎使用在某些稀有场合(基本上是不予考虑使用)

     B、bag/set在one-to-many中语义基本相同,推荐使用bag

     many-to-one:

     A、bag和set不同,bag允许重复插入,建议使用set

     =========================

在庞大的集合分页中应该使用session.createFilter

    session.createFilter(parent.getChildren(),""),setFirstResult(0),setMaxResult(10))

===================

避免N+1 参考(http://www.javaeye.com/post/266972)

    在多对一的情况下,查询child对象,当在页面上显示每个子类的父类对象的时候会导致N+1次查询,需要采用下面的方法避免:many-to-one fetch="join|select"(该方法可能有问题)

=========================

inverse=true 无法维护集合缓存(还不是很理解集合缓存和对象缓存)

OLTP类型的web应用,可以群集水平扩展,不可避免的出现数据库瓶颈

    框架能降低访问数据库的压力,采用缓存是衡量一个框架是否优秀的重要标准,从缓存方面看Hibernate

    A、对象缓存,细颗粒度,是针对表的级别,透明化访问,因为有不改变代码的好处,所以是ORM提高性能的法宝

    B、Hibernate是目前ORM框架中缓存性能最好的框架

    C、查询缓存

===============================

最后Robbin还针对大家经常出现的Hibernate vs iBatis的讨论进行了一个总结:

   对于OLTP应用,使用ORM框架 而OLEB应用(不确定是什么应用)最好采用JDBC或者其他方法处理

   Hibernate倾向于细颗粒度设计,面向对象,将大表拆分为多个小表,消除冗余字段,通过二级缓存提升性能。

  iBatis倾向于粗颗粒度设计,面向关系,尽量把表合并,通过Column冗余,消除关联关系,但是iBatis没有有效的缓存手段。

   可以说Robbin的性能总结对于使用Hibernate的开发人员有着很重要的点拨作用。非常感谢他无私奉献自己的经验。

有很多人认为Hibernate天生效率比较低,确实,在普遍情况下,需要将执行转换为SQL语句的Hibernate的效率低于直接JDBC存取,然 而,在经过比较好的性能优化之后,Hibernate的性能还是让人相当满意的,特别是应用二级缓存之后,甚至可以获得比较不使用缓存的JDBC更好的性能,下面介绍一些通常的Hibernate的优化策略:

    1.抓取优化

    抓取是指Hibernate如何在关联关系之间进行导航的时候,Hibernate如何获取关联对象的策略,其主要定义了两个方面:如何抓取和何时抓取

    1)如何抓取。

    Hibernate3主要有两种种抓取方式,分别应用于对象关联实例(many-to-one、one-to-one)和对象关联集合(set、map等),总共是四种变种

    JOIN抓取: 通过在SELECT语句中使用OUTER JOIN来获得对象的关联实例或者关联集合)

    SELECT抓取: 另外发送一条SELECT语句来抓取当前对象的关联实体和集合

    在我的开发经历中,此处对性能的优化是比较有限的,并不值得过多关注

    例:

    A.应用于对象关联实例(默认是false)

    <many-to-one name=".." outer-join="true/false/auto"  .../> 

    B.应用于对象关联集合(默认是auto)

    <set name=".." fetch="join/select" ... >

       ....

    </set>

    2)何时抓取

主要分为延迟加载和立即抓取,默认的情况下Hibernate3对对象关联实采用延迟加载,普通属性采用立即抓取,通过延迟加载和采用适当的抓取粒度,与不采用优化相比往往可以将性能提升数倍

    立即抓取:当抓取宿主对象时,同时抓取其关联对象和关联集以及属性

    延迟加载:当抓取宿主对象时,并不抓取其关联对象,而是当对其对象进行调用时才加载

    例:

    A.应用于对象关联实例(默认是延迟加载)

    <many-to-one name=".."  lazy="true/false" .../> 

    B.应用于对象关联集合(默认是延迟加载)

    <set name=".." lazy="true/false" ... >

       ....

    </set>

     对于延迟加载,需要注意的时,对延迟对象的使用必须在Session关闭之前进行,Hibernate的 LazyInitalizationException往往就是由于在Session的生命期外使用了延迟加载的对象。当我们进行Web开发时,可以使用 OpenSessionInView模式,当请求开始时打开session,当请求响应结束时才关闭session,不过,在使用 OpenSessionInView模式时,需要注意如果响应时间比较长(业务比较复杂或者客户端是低速网络),将Session资源(也就是数据库的连
接)占用太久的话可以会导致资源耗尽

3)抓取粒度

    抓取粒度指的是对象在关联关系之间被导航时一次预先加载的数量,Hibernate程序的性能比较差往往就在于没有对抓取粒度仔细考虑,当加载一个列表并在列表中的每个对象中对其关联进行导航时,往往导致N+1条SQL语句查询。

    例:

    A.应用于对象关联实例(默认为1),注意,对对象关联实例的设置是在被关联的对象之上的,譬如

    class User

    {

        Group g;

    }

    那么抓取粒度应该在Group的配置文件之上,见下

    <class name="Group" table="group" batch-size="..">

        ...

    </class>

    对该值并没有一个约定俗成的值,根据情况而定,如果被关联表数据比较少,则可以设置地小一些,3-20,如果比较大则可以设到30-50,注意的时候,并不是越多越好,当其值超过50之后,对性能并没有多大改善但却无谓地消耗内存

    假设有如下例子:

       List<User> users = query.list();

    如果有20个User,并对这20个User及其Group进行遍历,如果不设置batch-size(即batch-size="1"),则在最糟糕的情况

    下,需要1 + 20条SQL语句,如果设置batch-size="10",则最好的情况下只需要1 + 2条SQL语句

    B.应用于对象关联集合(默认为1)

<set name=".." batch-size="" ... >

       ....

    </set>

    2.二级缓存

    Hibernate 对数据的缓存包括两个级:一级缓存,在Session的级别上进行,主要是对象缓存,以其id为键保存对象,在Session的生命期间存在;二级缓存, 在SessionFactory的级别上进行,有对象缓存和查询缓存,查询缓存以查询条件为键保存查询结果,在SessionFactory的生命期间存 在。默认地,Hibernate只启用一级缓存,通过正确地使用二级缓存,往往可以获得意想不到的性能。

    1)对象缓存:

    当抓取一个对象之后,Hiberate将其以id为键缓存起来,当下次碰到抓取id相同的对象时,可以使用如下配置

    方法1:在缓存对象上配置

    <class ...>

       <cache useage="read-only/write/...." regions="group" />

    </class>

    useage 表示使用什么类型的缓存,譬如只读缓存、读写缓存等等(具体参见Hibernate参考指南),值得注意的时,有部分缓存在Hibernate的实现中不 支持读写缓存,譬如JBossCache在Hibernate的实现中只是一种只读缓存,具体缓存实现对缓存类型的支持情况,可以参见 org.hibernate.cache包

    regions表示缓存分块,大部分的缓存实现往往对缓存进行分块,该部分是可选的,详细参见各缓存实现

    方法2:在hibernate.cfg.xml中配置

    <cache class=".." useage=".." regions=".."/>

    我认为第二种更好,可以统一管理

2)查询缓存

    查询时候将查询结果以查询条件为键保存起来,需要配置如下

    A.在hibernate.cfg.xml中配置(启用查询缓存)

    <property name="hibernate.cache.use_query_cache">true</property>  (前面的属性名可参见常量

org.hibernate.cfg.Enviroment.USE_QUERY_CACHE)

    B.程序

    query.setCacheable(true);

    query.setCacheRegions(...);

    需要注意的是,查询缓存与对象缓存要结合更有效,因为查询缓存仅缓存查询结果列表的主键数据

     一般情况下在开发中,对一些比较稳定而又被频繁引用的数据,譬如数据字典之类的,将其进行二级缓存,对一些查询条件和查询数据变化不频繁而又常常被使用的 查询,将其进行二级缓存。由于二级缓存是放在内存中,而且Hibernate的缓存不是弱引用缓存(WeekReference),所以注意不要将大块的 数据放入其中,否则可能会被内存造成比较大的压力。

    3.批量数据操作

    当进行大批量数据操作(几万甚至几十几百万)时,需要注意两点,一,批量提交,二,及时清除不需要的一级缓存数据

    1) 所谓的批量提交,就是不要频繁使用session的flush,每一次进行flush,Hibernate将PO数据于数据库进行同步,对于海量级数据操 作来说是性能灾难(同时提交几千条数据和提交一条数据flush一次性能差别可能会是几十倍的差异)。一般将数据操作放在事务中,当事务提交时 Hibernate自动帮你进行flush操作。

    2)及时清除不需要的一级缓存数据:由于Hibernate默认采用一级缓存,而在 session的生命期间,所有数据抓取之后会放入一级缓存中,而当数据规模比较庞大时,抓取到内存中的数据会让内存压力非常大,一般分批操作数据,被一 次操作之后将一级缓存清除,譬如

    session.clear(User.class)

    4.杂项

    dynamic-insert,dynamic-update,动态插入和动态更新,指的是让Hibernate插入数据时仅插入非空数据,当修改数据时只修改变化的数据,譬如对于 

    class User

    {

       id

       username

       password

    }

     如果u.id=1, u.username="ayufox",u.password=null,那么如果不设置动态插入,则其sql语句是 insert into users(id, username, password) values (1, 'ayufox', '),如果设置则其 sql语句是insert into users(username) valeus('ayufox')

    在如上的情况下,如果修改 u.password='11',那么如果不设置动态更新,则sql语句为update users set username='ayufox',  password='11' where id = 1,如果设置则为update user set password='11' where d  = 1

    设置是在class的映射文件中,如下

    <class name="User" table="users" dynamic=insert="true/false" dynamic-update="true/false" ...>

    </class>

  该设置对性能的提升比较有限

==============================================

Hibernate性能优化(2)    left join user.messages msg 

group by user.id,user.name 

having count(msg)>=1 

===================================================================

文章分为十三个小块儿对Hibernate性能优化技巧进行总结性分析,分析如下:

一、在处理大数据量时,会有大量的数据缓冲保存在Session的一级缓存中,这缓存大太时会严重显示性能,所以在使用Hibernate处理大数据量的,可以使用session. clear()或者session. evict(Object) 在处理过程中,清除全部的缓存或者清除某个对象。

二、对大数据量查询时,慎用list()或者iterator()返回查询结果,

<1>. 使用List()返回结果时,Hibernate会所有查询结果初始化为持久化对象,结果集较

大时,会占用很多的处理时间。

<2>. 而使用iterator()返回结果时,在每次调用iterator.next()返回对象并使用对象时,

Hibernate才调用查询将对应的对象初始化,对于大数据量时,每调用一次查询都会花

费较多的时间。当结果集较大,但是含有较大量相同的数据,或者结果集不是全部都会

使用时,使用iterator()才有优势。

<3>. 对于大数据量,使用qry.scroll()可以得到较好的处理速度以及性能。而且直接对结

果集向前向后滚动。

三、对于关联操作,Hibernate虽然可以表达复杂的数据关系,但请慎用,使数据关系较为

简单时会得到较好的效率,特别是较深层次的关联时,性能会很差。

四、对含有关联的PO(持久化对象)时,若default-cascade="all"或者 “save-update”,新增PO时,请注意对PO中的集合的赋值操作,因为有可能使得多执行一次update操作。

五、在一对多、多对一的关系中,使用延迟加载机制,会使不少的对象在使用时才会初始化,这样可使得节省内存空间以及减少数据库的负荷,而且若PO中的集合没有被使用时,就可减少互数据库的交互从而减少处理时间。

六、对于大数据量新增、修改、删除操作或者是对大数据量的查询,与数据库的交互次数是决定处理时间的最重要因素,减少交互的次数是提升效率的最好途径,所以在开发过程中,请将show_sql设置为true,深入了解Hibernate的处理过程,尝试不同的方式,可以使得效率提升。

七、Hibernate是以JDBC为基础,但是Hibernate是对JDBC的优化,其中使用Hibernate的缓冲机制会使性能提升,如使用二级缓存以及查询缓存,若命中率较高明,性能会是到大幅提升。

八、Hibernate可以通过设置hibernate.jdbc.fetch_size,hibernate.jdbc.batch_size等属性,对Hibernate进行优化。

九、不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,大量并发insert数据时可能会引起表之间的互锁。数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响。因此,对于并发Insert要求较高的系统,推荐采用uuid.hex
作为主键生成机制。

十、Dynamic Update 如果选定,则生成Update SQL 时不包含未发生变动的字段属性,这样可以在一定程度上提升SQL执行效能.Dynamic Insert 如果选定,则生成Insert SQL 时不包含未发生变动的字段属性,这样可以在一定程度上提升SQL执行效能

十一、在编写代码的时候请,对将POJO的getter/setter方法设定为public,如果设定为private,Hibernate将无法对属性的存取进行优化,只能转而采用传统的反射机制进行操作,这将导致大量的性能开销(特别是在1.4之前的Sun JDK版本以及IBM JDK中,反射所带来的系统开销相当可观)。

十二、在one-to-many 关系中,将many 一方设为主动方(inverse=false)将有助性能的改善。

十三、由于多对多关联的性能不佳(由于引入了中间表,一次读取操作需要反复数次查询),因此在设计中应该避免大量使用。

以上便是对Hibernate性能优化技巧总结,希望能对您有所帮助。

========================================================

批量删除和修改

 

Hibernate是面向对象的,如果要对数据进行修改要先加载对象后修改,这样的操作对于修改单条数据是没什么问题的。如果要更新批量数据,就可能占用大量系统内存、需要执行多条select/update语句导致系统性能严重下降。所以在需要批量更新数据的时候,最好不要用上述方法。那么有什么更好方法么?那就是使用HQL语句。

HQL语句可以像用SQL那样进行批量操作数据。使用Hql就可以一次更新多条数据,而不用每次只更新一条数据了。

 

如:

更新User

Session session=factory.openSession();

       Transaction tx=null;

       try {

           tx=session.beginTransaction();

           //批量更新User

           String hql="update User u set u.age=20 where u.age<=18";

           //批量删除,String hql=”delete User u where u.age>25”;   

           Query query=session.createQuery(hql);

           int result=query.executeUpdate();        

           tx.commit();

       } catch (HibernateException e) {

           if(tx!=null) tx.rollback();

           // TODO Auto-generated catch block

           e.printStackTrace();

       }finally{

           session.close();

       }

 

 

选择确当的查询方法

Hibernatne 查询分为两类:一类是得到单个对象,get()和load方法;另一类是得到结果集,list()和iterator()方法。

get()方法和load()方法的区别在于对二级缓存的使用上。load()方法会使用二级缓存,而get()方法在一级缓存没有找到的情况下会直接查询数据库,不会去二级缓存中查找。在使用中,对使用了二级缓存的对象进行查询时最好使用load()方法,以充分利用二级缓存来提高检索的效率。

list()方法和iterator()方法之间的区别可以从以下几个方面来进行比较。

1、  执行的查询不同

list()方法在执行时,是直接运行查询结果所需要的查询语句,而iterator()方法则是先执行得到对象ID的查询,然后再根据每个ID值去取得所要查询的对象。因此,对于list()方式的查询通常只会执行一个SQL语句,而对于iterator()方法的查询则可能需要执行N+1条SQL语句(N为结果集中的记录数)。

iterator()方法只是可能执行N+1条数据,具体执行SQL语句的数量取决于缓存的情况以及对结果集的访问情况。

 

2、  缓存的使用

list()方法只能使用二级缓存中的查询缓存,而无法使用二级缓存对单个对象的缓存(但是会把查询出的对象放入二级缓存中)。所以,除非重复执行相同的查询操作,否则无法利用缓存的机制来提高查询的效率。

iterator()方法则可以充分利用二级缓存,在根据ID检索对象的时候会首先到缓存中查找,只有在找不到的情况下才会执行相应的查询语句。所以,缓存中对象的存在与否会影响到SQL语句的执行数量。

 

3、  对于结果集的处理方法不同

list()方法会一次获得所有的结果集对象,而且它会依据查询的结果初始化所有的结果集对象。这在结果集非常大的时候必然会占据非常多的内存,甚至会造成内存溢出情况的发生。

iterator()方法在执行时不会一次初始化所有的对象,而是根据对结果集的访问情况来初始化对象。因此在访问中可以控制缓存中对象的数量,以避免占用过多缓存,导致内存溢出情况的发生。使用iterator()方法的另外一个好处是,如果只需要结果集中的部分记录,那么没有被用到的结果对象根本不会被初始化。所以,对结果集的访问情况也是调用iterator()方法时执行数据库SQL语句多少的一个因素。

所以,在使用Query对象执行数据查询时应该从以上几个方面去考虑使用何种方法来执行数据库的查询操作。

=========百度文库资料的hibernate优化======================

优化总结?

???????????

要想优化Hibernate,我们必须知道应该从什么地方进行优化,从什么地方入手。Hibernate的优化方向:数据库设计、HQL优化、缓存、主配置、延迟加载、方法选用、集合选用、事物控制、批量操作 

第一点:数据库设计?

???????前边博客介绍过?数据库设计,今天我们还从这开始,表的设计就是建楼的基础,如何让基础简洁而结实这是最重要的。

优化策略:?1.???建索引?

2.???减少表之间的关联?

3.???简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据?4.???适当的冗余数据,不过分最求高范式.

第二点:HQL优化 

 

第三点:缓存?

运行机制:?

介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频数,从而提高运行性能。?

缓存被广泛应用的用于优化数据库。当一些数据被从数据库中读取出来的时候,我们可以把它们放到缓存里。这样我们可以再次使用的时候直接从缓存中取出来,这样我们的效率就提高了很多。?

控制范围:?

一级缓存是session对象的生命周期通常对应的一个数据库事务或者一个应用事务,它是事务范围内的缓存

二级缓存是一个可插拔的缓存插件,它是由SessionFactory负责管理。由于SessionFactory对象的生命周期和应用程序的整个过程对应,所以二级缓存是进城范围或者集群范围内的缓存。用于初始化很少更改的数据、不重要的数据,不会并发访问的数据。

Hibernate缓存的一些问题和建议:hibernate的缓存?

第四点:捉取策略?

1.??捉取优化:Hibernate在关联关系之间进行导航,充分利用Hibernate提供的技术?

2.??如何捉取?

立即捉取:当捉取宿主对象时,同时捉取其相关对象和关联集以及属性?

延迟加载:当捉宿主对象时,并不捉取其关联对象,而是当对其对象进行调用时才加载。?

3.??捉取粒度:设置捉取个数?

第五点:批量数据处理(修改和删除)?

在Hibernate2中,如果需要对任何数据进行修改和删除操作都需要先执行查询操作,在得到数据后才进行修改和删除。?

1.??不适用Hibernate?API而是直接使用JDBC??API来做原生态SQL语句进行查询,这种方法比较好,相对来说较快。

2.??运用存储过程?

3.??一定量范围内可以使用hibernate?API,但是特大数据量不行。

第六点:结果集的使用:?

?????

结果集的使用:list()和iterator()区别?查询方式:?

list只能利用查询缓存(但在交易系统中查询缓存作用不大),无法利用二级缓存中的单个实体,但是list查出的对象会写入二级缓存,但它一般只生成较少的sql语句,很多情况就是一条。?

iterator则利用二级缓存,对于一条查询语句,它会先从数据库中找到所有符合条件的记录的ID,在通过ID去缓存找,对于缓存中没有的记录,在构造语句从数据库查出,第一次的执行会产生N+1条SQL语句。?产生结果:?用list可能会溢出?

通过Iterator,配合缓存管理API,在海量数据查询中可以很好的解决内存问题。?综合考虑:?

一般List会填充二级缓存,却不能利用二级缓存,而Iterator可以读二级缓存,然而无法命中的话,效率很低效。一般处理方法,就是第一次查询使用list,随后使用iterator查询。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  hibernate