`

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。
    
 /**
     * 测试成批插入数据的事务处理,返回是否成功
     *
     *  @param objPO Object
     *  @return boolean
     */
 public boolean insertBatch(final Object objPO) {
		boolean isSuccess = false;
		Transaction transaction = null;
		Session session = this.getSession();
		try {
			transaction = session.beginTransaction();
			for (int i = 0; i < 100000; i++) {
				session.save(objPO);
				if (i % 50 == 0) {
					// flush a batch of inserts and release memory
					session.flush();
					session.clear();
				}
			}
			transaction.commit();
			logger.info(" transaction.wasCommitted: "
					+ transaction.wasCommitted());
			isSuccess = true;
		} catch (HibernateException ex) {
			if (transaction != null) {
				try {
					transaction.rollback();
					logger.error(" transaction.wasRolledBack: "
							+ transaction.wasRolledBack());
				} catch (HibernateException ex1) {
					logger.error(ex1.getMessage());
					ex1.printStackTrace();
				}
			}
			logger.error(" Insert Batch PO Error: " + ex.getMessage());
			ex.printStackTrace();
		} finally {
			if (transaction != null) {
				transaction = null;
			}
			session.close();
		}
		return isSuccess;
	}
      

      这只是简单的测试,实际项目中遇到的问题,要比这个复杂得多。
      这时候,我们可以让Spring来控制Transaction,自己来控制Hibernate的Session,随时更新数据。
      首先,利用HibernateDaoSupport类来自定义个方法打开Session;
    
 public Session openSession(){
      
       return getHibernateTemplate().getSessionFactory().openSession();
      
          }

      然后,用打开的Session处理你的数据;
     
protected void doBusiness(Session session) {
      
       while (true) {
       //do your business with the opening session
                  someMethod(session);
                  session.flush();
                  session.clear();
                  logger.info("good job!");
              }
      }

      每做一次数据操作,就更新一次Session,这样可以保证每次数据操作都成功,否则就让Spring去控制它roll back吧。
      最后,记得关闭Session。
    
  Session session  =  openSession();
       doBusiness(session);
       session.close();  // 关闭session
分享到:
评论

相关推荐

    Hibernate下数据批量处理解决方案

    在Java开发中,尤其是涉及到大数据量的处理时,人们往往会质疑ORM框架,如Hibernate,是否适合进行批量数据操作。然而,实际上,通过适当的技术手段,我们可以有效地解决Hibernate在批量处理时可能出现的性能问题。...

    Hibernate性能优化研究.pdf

    然而,在实际应用过程中,如何有效地利用Hibernate来优化数据持久层的性能仍然是一大挑战。本文旨在通过对Hibernate的深入研究,探讨影响其性能的各种因素,并提出相应的优化策略。 #### 二、O/R Mapping及...

    使用 Hibernate Shards 对数据进行切分

    《使用Hibernate Shards对数据...总结,Hibernate Shards是应对大数据量和高并发场景的有效工具,通过数据分片提高数据库的处理能力。理解和熟练运用Hibernate Shards,能帮助开发者构建更加健壮、可扩展的分布式系统。

    Hibernate批量处理数据

    当对象数量过大时,会导致内存不足。为了解决这一问题,可以通过以下方式: 1. **定时刷新缓存**:在循环过程中,设定一定的阈值(如每20条记录),当达到该阈值时,使用`session.flush()`将缓存中的数据写入数据库...

    Hibernate进行数据查询

    在Java开发中,Hibernate是一个流行的持久化框架,它简化了数据库操作,使得开发者可以使用面向对象的方式来处理数据。本篇将详细阐述如何使用Hibernate进行数据查询,包括基本数据查询、HQL(Hibernate Query ...

    hibernate中处理大字段 网上收集的文档

    处理大字段时,由于数据量较大,可能会导致事务持续时间较长,增加死锁的风险。因此,合理设计事务边界,避免长时间持有事务,有助于提高系统并发性能。 总结来说,Hibernate处理大字段涉及到数据类型的选用、映射...

    浅析Hibernate下数据批量处理方法.doc

    然而,对于大数据量的批量处理,Hibernate的性能可能不如直接使用JDBC或其他低级数据库操作高效。这是因为Hibernate在默认情况下会缓存对象,以便在事务提交时进行一次性持久化,这可能导致内存消耗过大,尤其是在...

    优化Hibernate性能的几点建议

    这种方式特别适用于需要分批处理大数据量的情况。 #### 五、合理管理实体之间的关联关系 在设计模型时,需要注意实体之间的关联关系。过度使用多对多或多对一的关系可能会导致大量的N+1查询问题。为了避免这种情况...

    hibernate对Blob类型字段进行数据添加.txt

    在处理数据库操作时,经常需要存储二进制大对象(Binary Large Object,简称 BLOB),例如图像、视频等非文本数据。BLOB 类型是数据库系统中用于存储大量二进制数据的数据类型。在 Java 应用开发中,尤其是使用 ORM ...

    hibernate代码优化基于配置的多表查询

    在Java的Web开发中,Hibernate是一个非常流行的ORM(对象关系映射)框架,它使得开发者可以使用面向对象的方式来操作数据库,极大地提高了开发效率。本文将深入探讨如何通过配置优化Hibernate的多表查询,以提高应用...

    Hibernate优化

    然而,随着项目规模的扩大,如果不进行适当的优化,Hibernate可能会成为性能瓶颈。本文将深入探讨Hibernate的优化策略,帮助开发者提升应用性能。 一、配置优化 1. **连接池配置**:选择合适的连接池如HikariCP或...

    Hibernate缓存,性能优化

    - **异步更新策略**:采用异步更新机制,将频繁更新的数据操作排队处理,避免实时更新对性能的影响,同时确保数据的最终一致性。 #### 监控和调优 - **性能监控工具**:利用JProfiler、VisualVM等工具,定期分析...

    struts2+hibernate实现登录及增删改操作

    比如,合理使用缓存,避免频繁打开和关闭Session,使用预编译的SQL语句(PreparedStatement),以及对大数据量操作进行分批处理等。 综上所述,"struts2+hibernate实现登录及增删改操作"涉及到的是如何利用这两个...

    配置hibernate数据源

    配置Hibernate数据源是一项基础而重要的任务,涉及在Java应用程序中集成和使用Hibernate框架以及MySQL数据库。以下内容旨在详细介绍如何在MyEclipse开发环境中配置Hibernate数据源,同时解释相关概念和步骤。 首先...

    hibernate的大部分的工具类

    总结来说,这个压缩包中的工具类覆盖了Hibernate的多个方面,包括初始化设置、数据验证、分页处理以及HQL查询的优化。它们是后端开发中的重要组成部分,特别是对于涉及持久层操作的项目,这些工具类将极大地增强代码...

    Hibernate批量处理

    - **二级缓存管理**:在进行批量操作时,建议关闭Hibernate的二级缓存,以避免缓存同步问题。这可以通过在配置文件中设置`hibernate.cache.use_second_level_cache=false`来实现。 示例代码展示了一个批量插入十万...

    hibernate性能优化方案

    2. **Fetch Size和Batch Size**:这两个参数分别控制了每次从数据库读取的数据量和批量处理的数据量,合理设置可以提高效率。 3. **关闭SQL语句打印**:在生产环境中,关闭Hibernate的日志输出可以减少不必要的I/O...

Global site tag (gtag.js) - Google Analytics