`

在项目中使用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进行大数据量的性能测试,有一些总结, 1) 在处理大数据量时,会有大量的数据缓冲保存在Session的一级缓存中 2) 对大数据量查询时,慎用list()或者iterator()返回查询结果, 3) 对于关联操作...

    Hibernate实战(第2版 中文高清版)

     1.1.3 在Java中使用SQL   1.1.4 面向对象应用程序中的持久化   1.2 范式不匹配   1.2.1 粒度问题   1.2.2 子类型问题   1.2.3 同一性问题   1.2.4 与关联相关的问题   1.2.5 数据导航的问题   ...

    最全Hibernate 参考文档

    1. 在Tomcat中快速上手 1.1. 开始Hibernate之旅 1.2. 第一个持久化类 1.3. 映射cat 1.4. 与Cat同乐 1.5. 结语 2. 架构(Architecture) 2.1. 概况(Overview) 2.2. 实例状态 2.3. JMX整合 2.4. 对JCA的支持 3. 配置 ...

    Hibernate教程

    19.1. 用XML数据进行工作 19.1.1. 指定同时映射XML和类 19.1.2. 只定义XML映射 19.2. XML映射元数据 19.3. 操作XML数据 20. 提升性能 20.1. 抓取策略(Fetching strategies) 20.1.1. 操作延迟加载的关联 20.1.2...

    hibernate3.04中文文档.chm

    19.1. 用XML数据进行工作 19.1.1. 指定同时映射XML和类 19.1.2. 只定义XML映射 19.2. XML映射元数据 19.3. 操作XML数据 20. 提升性能 20.1. 抓取策略(Fetching strategies) 20.1.1. 操作延迟加载的关联 ...

    Hibernate3+中文参考文档

    18.1. 用XML数据进行工作 18.1.1. 指定同时映射XML和类 18.1.2. 只定义XML映射 18.2. XML映射元数据 18.3. 操作XML数据 19. 提升性能 19.1. 抓取策略(Fetching strategies) 19.1.1. 调整抓取策略(Tuning fetch ...

    db-util:如果您使用的是JPA和Hibernate,则此工具可以在测试过程中自动检测N + 1个查询问题

    此外,借助Hypersistence Optimizer,您可以在测试过程中检测到所有此类问题,并确保不会将影响数据访问层性能的更改部署到生产环境中。 是您期待已久的工具!训练如果您对现场培训感兴趣,我可以为您提供

    hibernate 框架详解

    用XML数据进行工作 19.1.1. 指定同时映射XML和类 19.1.2. 只定义XML映射 19.2. XML映射元数据 19.3. 操作XML数据 20. 提升性能 20.1. 抓取策略(Fetching strategies) 20.1.1. 操作延迟加载的关联 ...

    jpa-hibernate-jdbc:比较同一个数据库的JPA,Hibernate和JDBC的性能

    背景开发人员普遍担心,使用工具(ORM)将严重影响其应用程序的性能,尤其是在数据库包含大量记录或需要批量获取数据的情况下。 一些开发人员创建了工作基准,表明使用ORM与使用原始JDBC相比,可导致性能下降10或100...

    JAVA上百实例源码以及开源项目源代码

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    CSHibernate .Net轻量通用数据层框架(全部源码)

    sql2005的已在实际项目中使用,oracle的一直没时间测试-_-||,见谅。 另外又改了一些,有兴趣的朋友可以通过邮件向我索要最新版源码 email:173023135@qq.com ------------------------------------------- 发现几个...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    友情链接及网页访问量统计显示:在博客的个人页面中还提供了推荐给普通网络用户的相关友情链接,此外,对个人页面的访问量也在随时进行统计,并在个人页面中进行直观的显示。 博客主页面的用例图如图3所示: 图3 ...

    基于J2EE框架的个人博客系统项目毕业设计论...

    友情链接及网页访问量统计显示:在博客的个人页面中还提供了推荐给普通网络用户的相关友情链接,此外,对个人页面的访问量也在随时进行统计,并在个人页面中进行直观的显示。 博客主页面的用例图如图3所示: 图3 ...

    低清版 大型门户网站是这样炼成的.pdf

    2.5.1 在action中实现手动校验 90 2.5.2 调用校验框架进行自动校验 91 2.5.3 自定义国际化struts 2校验错误消息 92 2.5.4 struts 2的自带校验器 92 2.6 struts 2的拦截器 94 2.6.1 struts 2内建拦截器介绍 95 ...

    工程硕士学位论文 基于Android+HTML5的移动Web项目高效开发探究

    目前市场业务中在产品以及其他项目的认证和检测方面存在诸多不便,用户需要实地考察并频繁与检测单位沟通,填写繁琐的纸质检测报告、当面送递样品,对于检测环节中存在的问题难以及时交互并处理。市场上相应的检测...

    JSP开发技术大全 源码

    软件工程师典藏•JSP开发技术大全》是一本JSP综合开发参考手册,书中几乎囊括了使用JSP进行实用程序开发的全部知识,同时在讲解中结合了大量实用而又有代表性的示例和典型应用。全书共分9篇32章,分别介绍了JSP基础...

    JSP开发技术大全 JSP

    本书是一本JSP综合开发参考手册,书中几乎囊括了使用JSP进行实用程序开发的全部知识,同时在讲解中结合了大量实用而又有代表性的示例和典型应用。全书共分9篇32章,分别介绍了JSP基础、环境搭建和开发工具、Java语言...

    java-ee电子商城系统课程设计.doc

    Hibernate 可以应用在任何使用JDBC 的场合, 既可以在Java 的客户端程序使用, 也可以在Servlet/JSP 的Web 应用中使用。 AJAX是Asynchronout JavaScript and XML的缩写,它并不是一门新的语言或技术,实际上是几项...

Global site tag (gtag.js) - Google Analytics