`

在项目中使用Hibernate进行大数据量的性能测试

 
阅读更多

 在项目中使用Hibernate进行大数据量的性能测试,有一些总结,
    1) 在处理大数据量时,会有大量的数据缓冲保存在Session的一级缓存中,这缓存大太时会严重显示性能,所以在使用Hibernate处理大数据量的,可以使用session.clear()或者session. Evict(Object) 在处理过程中,清除全部的缓存或者清除某个对象。
    2) 对大数据量查询时,慎用list()或者iterator()返回查询结果,
    1. 使用List()返回结果时,Hibernate会所有查询结果初始化为持久化对象,结果集较大时,会占用很多的处理时间。
    2. 而使用iterator()返回结果时,在每次调用iterator.next()返回对象并使用对象时,Hibernate才调用查询将对应的对象初始化,对于大数据量时,每调用一次查询都会花费较多的时间。当结果集较大,但是含有较大量相同的数据,或者结果集不是全部都会使用时,使用iterator()才有优势。
    3. 对于大数据量,使用qry.scroll()可以得到较好的处理速度以及性能。而且直接对结果集向前向后滚动。
    3) 对于关联操作,Hibernate虽然可以表达复杂的数据关系,但请慎用,使数据关系较为简单时会得到较好的效率,特别是较深层次的关联时,性能会很差。
    4) 对含有关联的PO(持久化对象)时,若default-cascade="all"或者 “save-update”,新增PO时,请注意对PO中的集合的赋值操作,因为有可能使得多执行一次update操作。
    5) 在一对多、多对一的关系中,使用延迟加载机制,会使不少的对象在使用时方会初始化,这样可使得节省内存空间以及减少的负荷,而且若PO中的集合没有被使用时,就可减少互数据库的交互从而减少处理时间。  数据库
    什么叫n+1次select查询问题?
    在Session的缓存中存放的是相互关联的对象图。默认情况下,当Hibernate从数据库中加载Customer对象时,会同时加载所有关联的Order对象。以Customer和Order类为例,假定ORDERS表的CUSTOMER_ID外键允许为null,图1列出了CUSTOMERS表和ORDERS表中的记录。
    以下Session的find()方法用于到数据库中检索所有的Customer对象:
    List customerLists=session.find("from Customer as c");
    运行以上find()方法时,Hibernate将先查询CUSTOMERS表中所有的记录,然后根据每条记录的ID,到ORDERS表中查询有参照关系的记录,Hibernate将依次执行以下select语句:
    select * from CUSTOMERS;
    select * from ORDERS where CUSTOMER_ID=1;
    select * from ORDERS where CUSTOMER_ID=2;
    select * from ORDERS where CUSTOMER_ID=3;
    select * from ORDERS where CUSTOMER_ID=4;
    通过以上5条select语句,Hibernate最后加载了4个Customer对象和5个Order对象,在内存中形成了一幅关联的对象图,参见图2。
    Hibernate在检索与Customer关联的Order对象时,使用了默认的立即检索策略。这种检索策略存在两大不足:
    (a) select语句的数目太多,需要频繁的访问数据库,会影响检索性能。如果需要查询n个Customer对象,那么必须执行n+1次select查询语句。这就是经典的n+1次select查询问题。这种检索策略没有利用SQL的连接查询功能,例如以上5条select语句完全可以通过以下1条select语句来完成:
    select * from CUSTOMERS left outer join ORDERS
    on CUSTOMERS.ID=ORDERS.CUSTOMER_ID
    以上select语句使用了SQL的左外连接查询功能,能够在一条select语句中查询出CUSTOMERS表的所有记录,以及匹配的ORDERS表的记录。
    (b)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间。
    为了解决以上问题,Hibernate提供了其他两种检索策略:延迟检索策略和迫切左外连接检索策略。延迟检索策略能避免多余加载应用程序不需要访问的关联对象,迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数目。
    刚查阅了hibernate3的文档:
    查询抓取(默认的)在N+1查询的情况下是极其脆弱的,因此我们可能会要求在映射文档中定义使用连接抓取:
    <set name="permissions"
    fetch="join">
    <key column="userId"/>
    <one-to-many class="Permission"/>
    </set
    <many-to-one name="mother" class="Cat" fetch="join"/>
    在映射文档中定义的抓取策略将会有产生以下影响:
    通过get()或load()方法取得数据。
    只有在关联之间进行导航时,才会隐式的取得数据(延迟抓取)。
    条件查询
    在映射文档中显式的声明 连接抓取做为抓取策略并不会影响到随后的HQL查询。
    通常情况下,我们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,然后在特定的事务中, 使用HQL的左连接抓取(left join fetch) 对其进行重载。这将通知 Hibernate在第一次查询中使用外部关联(outer join),直接得到其关联数据。 在条件查询 API中,应该调用 setFetchMode(FetchMode.JOIN)语句。
    6) 对于大数据量新增、修改、删除操作或者是对大数据量的查询,与数据库的交互次数是决定处理时间的最重要因素,减少交互的次数是提升效率的最好途径,所以在开发过程中,请将show_sql设置为true,深入了解Hibernate的处理过程,尝试不同的方式,可以使得效率提升。
    7) Hibernate是以JDBC为基础,但是Hibernate是对JDBC的优化,其中使用Hibernate的缓冲机制会使性能提升,如使用二级缓存以及查询缓存,若命中率较高明,性能会是到大幅提升。
    8) Hibernate可以通过设置hibernate.jdbc.fetch_size,hibernate.jdbc.batch_size等属性,对Hibernate进行优化。
    hibernate.jdbc.fetch_size 50
    hibernate.jdbc.batch_size 25
    这两个选项非常非常非常重要!!!将严重影响Hibernate的CRUD性能!
    C = create, R = read, U = update, D = delete
    Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。

  例如一次查询1万条记录,对于Oracle的JDBC驱动来说,是不会1次性把1万条取出来的,而只会取出Fetch Size条数,当纪录集遍历完了这些记录以后,再去数据库取Fetch Size条数据。
    因此大大节省了无谓的内存消耗。当然Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。
    这有点像平时我们写程序写硬盘文件一样,设立一个Buffer,每次写入Buffer,等Buffer满了以后,一次写入硬盘,道理相同。
    Oracle数据库的JDBC驱动默认的Fetch Size=10,是一个非常保守的设定,根据我的测试,当Fetch Size=50的时候,性能会提升1倍之多,当Fetch Size=100,性能还能继续提升20%,Fetch Size继续增大,性能提升的就不显著了。
    因此我建议使用Oracle的一定要将Fetch Size设到50。
    不过并不是所有的数据库都支持Fetch Size特性,例如MySQL就不支持。
    MySQL就像我上面说的那种最坏的情况,他总是一下就把1万条记录完全取出来,内存消耗会非常非常惊人!这个情况就没有什么好办法了 :(
    Batch Size是设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,有点相当于设置Buffer缓冲区大小的意思。
    Batch Size越大,批量操作的向数据库发送sql的次数越少,速度就越快。我做的一个测试结果是当Batch Size=0的时候,使用Hibernate对Oracle数据库删除1万条记录需要25秒,Batch Size = 50的时候,删除仅仅需要5秒!!!
    //
    我们通常不会直接操作一个对象的标识符(identifier),因此标识符的setter方法应该被声明为私有的(private)。这样当一个对象被保存的时候,只有Hibernate可以为它分配标识符。你会发现Hibernate可以直接访问被声明为public,private和protected等不同级别访问控制的方法(accessor method)和字段(field)。 所以选择哪种方式来访问属性是完全取决于你,你可以使你的选择与你的程序设计相吻合。
    所有的持久类(persistent classes)都要求有无参的构造器(no-argument constructor);因为Hibernate必须要使用Java反射机制(Reflection)来实例化对象。构造器(constructor)的访问控制可以是私有的(private),然而当生成运行时代理(runtime proxy)的时候将要求使用至少是package级别的访问控制,这样在没有字节码编入(bytecode instrumentation)的情况下,从持久化类里获取数据会更有效率一些。
    而
    hibernate.max_fetch_depth 设置外连接抓取树的最大深度
    取值。 建议设置为0到3之间
    就是每次你在查询时,会级联查询的深度,譬如你对关联vo设置了eager的话,如果fetch_depth值太小的话,会发多很多条sql
    Hibernate的Reference之后,可以采用批量处理的方法,当插入的数据超过10000时,就flush session并且clear。
    下面是一个测试method。
    1   /** */ /**
    2      * 测试成批插入数据的事务处理,返回是否成功
    3      *
    4      *  @param objPO Object
    5      *  @return boolean
    6  */
    7   public boolean  insertBatch( final  Object objPO)   {
    8  boolean  isSuccess  = false ;
    9         Transaction transaction  = null ;
    10         Session session  = openSession();
    11   try  {
    12             transaction  = session.beginTransaction();
    13   for  ( int  i  = 0 ; i  < 100000 ; i ++ )   {
    14                 session.save(objPO);
    15   if  (i  % 50 == 0 )   {
    16  //  flush a batch of inserts and release memory
    17                      session.flush();
    18                     session.clear();
    19                 }
    20             }
    21             transaction.commit();
    22             logger.info( " transaction.wasCommitted: "
    23  + transaction.wasCommitted());
    24             isSuccess  = true ;
    25          } catch  (HibernateException ex)   {
    26   if  (transaction  != null )   {
    27   try  {
    28                     transaction.rollback();
    29                     logger.error( " transaction.wasRolledBack: "
    30  + transaction.wasRolledBack());
    31                  } catch  (HibernateException ex1)   {
    32                     logger.error(ex1.getMessage());
    33                     ex1.printStackTrace();
    34                 }
    35             }
    36             logger.error( " Insert Batch PO Error: " + ex.getMessage());
    37             ex.printStackTrace();
    38          } finally  {
    39   if  (transaction  != null )   {
    40                 transaction  = null ;
    41             }
    42             session.close();
    43         }
    44  return isSuccess;
    45     }
    46
    这只是简单的测试,实际项目中遇到的问题,要比这个复杂得多。
    这时候,我们可以让Spring来控制Transaction,自己来控制Hibernate的Session,随时更新数据。
    首先,利用HibernateDaoSupport类来自定义个方法打开Session;
    1public Session openSession(){
    2
    3 return getHibernateTemplate()。getSessionFactory()。openSession();
    4
    5    }
    然后,用打开的Session处理你的数据;
    1protected void doBusiness(Session session) {
    2
    3 while (true) {
    4 //do your business with the opening session
    5            someMethod(session);
    6            session.flush();
    7            session.clear();
    8            logger.info("good job!");
    9        }
    10}
    每做一次数据操作,就更新一次Session,这样可以保证每次数据操作都成功,否则就让Spring去控制它roll back吧。
    最后,记得关闭Session。
    1  Session session  =  openSession();
    2 doBusiness(session);
    3 session.close();  // 关闭session

 

分享到:
评论

相关推荐

    hibernate性能测试代码

    - **持久化性能**:保存、更新和删除操作的效率,尤其是在大数据量的情况下。 - **内存使用**:检查内存泄漏和内存占用情况,避免因为过度占用内存导致系统变慢或崩溃。 - **事务性能**:衡量事务处理能力,特别是...

    配置hibernate数据源

    Hibernate是一个独立的开源库,其特点在于不仅能在Web项目中使用,在普通的Java项目中也同样适用。它提供了映射原数据、查询语言和事务管理等机制,是Java领域内应用最为广泛的ORM框架之一。 MySQL数据库的安装和...

    hibernate测试

    **hibernate测试** ...通过以上内容的学习和实践,读者将能够熟练掌握Hibernate的使用,并能进行有效的测试,确保项目中的数据访问层稳定可靠。同时,欢迎读者们分享自己的经验和评价,共同提升对Hibernate的理解。

    Springmvc+hibernate+mysql练手项目

    MySQL提供了强大的性能和稳定性,适合处理大量数据的Web应用程序。 项目实践内容包括: 1. 用户管理:添加用户功能,这是通过创建一个表示用户的实体类,使用Hibernate的持久化机制将其保存到MySQL数据库中实现的。...

    Hibernate性能优化

    《Hibernate性能优化》 ...以上是Hibernate性能优化的一些核心点,实践中还需要结合具体项目进行调整和测试,找到最适合的优化策略。学习和理解这些知识点,有助于开发出更高效、更稳定的Java应用程序。

    使用Hibernate开发租房系统910

    在本教程"使用Hibernate开发租房系统910"中,我们将深入探讨如何利用Hibernate来构建一个功能完备的租房系统。这个教程可能属于ACCP6.0+(北大青鸟的高级软件开发课程)的一部分,旨在帮助学员提升实际项目开发能力...

    hibernate连接Access应用项目例子

    在本项目中,“hibernate连接Access应用项目例子”展示了如何使用Hibernate ORM框架与Microsoft Access数据库进行集成,实现数据的增、删、改、查(CRUD)操作。Hibernate是Java开发中的一个流行ORM(对象关系映射)...

    hibernate 单元测试批处理代码

    在这个“hibernate 单元测试批处理代码”项目中,我们将深入探讨如何使用Hibernate进行单元测试,并实现批处理操作,特别针对MySQL数据库。 首先,让我们理解Hibernate单元测试的基础。单元测试是针对软件中的最小...

    使用springmvc+hibernate完成增删改查

    在本项目中,"使用springmvc+hibernate完成增删改查"意味着开发者已经配置好了SpringMVC和Hibernate的环境,能够通过这两个框架进行数据操作。数据无需创建自动生成,可能指的是使用了Hibernate的自动建表功能,或者...

    SpringBoot+hibernate+mysql+sqlserver双数据源

    5. 测试与监控:为了确保双数据源的正确性和性能,需要进行充分的单元测试和集成测试,同时利用SpringBoot的Actuator模块进行监控,了解数据源的使用情况。 6. 安全性考虑:在实际部署中,需要确保数据库连接的安全...

    Hibernate缓存,性能优化

    - **持续集成测试**:在开发流程中集成性能测试环节,确保每次代码提交都不会引入性能退化的问题。 综上所述,Hibernate缓存机制是提升应用性能的重要手段,通过合理配置缓存策略、优化查询方式、管理数据库连接...

    Hibernate配置数据源.

    在软件开发过程中,特别是涉及大量数据库交互的应用场景下,选择合适的数据源配置对于提高系统性能、稳定性具有重要意义。Hibernate作为Java领域内广泛使用的对象关系映射(ORM)工具之一,其灵活高效的数据源配置...

    myeclipse+hibernate项目配置包

    2. **Hibernate**: Hibernate是一个开源的Java ORM框架,它的主要作用是简化数据库操作,通过将Java对象映射到数据库表,使得开发者可以使用面向对象的方式进行数据库操作,避免了大量SQL语句的编写。Hibernate支持...

    hibernate jsp 分页Demo

    【hibernate jsp 分页Demo】是一个演示项目,展示了如何在Java Web应用程序中使用Hibernate框架结合JSP页面实现数据库查询的分页功能。在这个项目中,开发人员使用了MySQL作为后端数据库,通过Hibernate ORM工具与...

    strus2+hibernate论坛项目

    Struts2和Hibernate是两种非常重要的Java Web开发框架,它们在构建BBS(Bulletin Board System,电子公告板)论坛项目中发挥着核心作用。Struts2作为MVC(Model-View-Controller)架构的一部分,主要负责处理用户...

    基于Hibernate的数据持久层关键技术的研究

    随着软件开发的不断进步和技术的发展,越来越多的企业级应用需要高效地管理和访问大量数据。为了更好地满足这一需求,许多开发团队选择了使用ORM(对象关系映射)工具来简化数据库操作。其中,Hibernate作为一款流行...

    hibernate-性能优化

    【描述】:在使用 Hibernate 进行数据库操作时,性能问题时常出现,但通过适当的调整和优化,我们可以显著提高 Hibernate 的执行效率,甚至达到接近或超过直接使用 JDBC 的效果。本文将探讨几个关键的优化策略。 ...

    Accp6.0 Y2 Hibernate内部+阶段测试答案

    学习者需要通过"阶段测试"和"内部测试"来检测自己在实际项目中运用Hibernate的能力,这涵盖了从基本配置到复杂查询的各种场景。通过解压并分析提供的文件,可以深化对Hibernate的工作原理、最佳实践和问题解决策略的...

    Hibernate中的DAO模式

    **分页**是Web应用中常见的需求,它有助于提高用户体验,避免一次性加载大量数据导致页面响应慢。在Hibernate中,可以利用存储过程实现分页查询,也可以使用Criteria API或HQL(Hibernate Query Language)配合...

Global site tag (gtag.js) - Google Analytics