`
meiowei
  • 浏览: 147724 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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下数据批量处理解决方案

    总的来说,虽然ORM框架在处理大数据时可能存在性能挑战,但通过合理配置和使用批处理、游标滚动以及适时的缓存管理,我们可以克服这些挑战,使Hibernate在批量数据处理中发挥高效的作用。在实际项目中,还应结合...

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

    在Java的持久化框架Hibernate中,处理大数据字段,如文本、图像或者其他大量数据,通常涉及到CLOB(Character Large Object)和BLOB(Binary Large Object)类型。这些类型用于存储数据库中的大文本或二进制数据。...

    hibernate动态映射表处理oracle的clob类型

    - 分离大数据:如果可能,将CLOB数据存储在单独的表中,通过外键关联到主表,以优化查询性能。 - 使用Blob/Clob接口:直接操作Clob接口进行读写,而不是将其转换为字符串或其他数据类型,以减少转换开销。 通过...

    关于在Hibernate中对于Clob,Blob字段的处理方法

    在Java的持久化框架Hibernate中,处理大数据类型如Clob(Character Large Object)和Blob(Binary Large ...在实际开发中,了解并掌握这些知识点可以帮助你更高效、安全地处理大数据类型的存储和检索,提升应用性能。

    关于hibernate的批处理

    总结,Hibernate的批处理是大数据场景下提高性能的关键技术之一,需要根据具体业务需求和系统资源进行细致的配置和优化。了解并掌握这些知识点,能够帮助开发者更高效地利用Hibernate处理大规模数据。

    性能规模风险初评hibernate共4页.pdf.zip

    【标签】:“性能规模风险初评hibernat”进一步确认了文档的核心主题,即评估使用Hibernate可能带来的性能影响,如何应对大规模数据处理的挑战,以及如何识别和管理相关的技术风险。 【压缩包子文件的文件名称列表...

    Rapids大数据平台介绍.pptx

    Rapids大数据平台是一款针对现代数据密集型企业需求而设计的高效...它们在内存处理、数据分布、安全和性能等方面进行了优化,为企业提供了一站式的解决方案,以应对大数据时代的挑战,提高数据分析效率,降低运维成本。

    hibernate帮助文档 3.2、3.6、4.1

    3.6.10还提高了性能,尤其是在大数据集处理和并发场景下。 3. Hibernate 4.1.7: Hibernate 4.x引入了重大改进,首先是引入了Service Registry,以更好地管理和配置服务。4.1.7版本继续优化了这个特性,使得服务...

    Hibernate操作Oarcle中Clob、Blob字段小结

    在Java的持久化框架Hibernate中,处理大数据类型如Oracle数据库中的Clob(Character Large Object)和Blob(Binary Large Object)字段是一项重要的任务。Clob通常用于存储大量的文本数据,而Blob则适用于二进制...

    spring1.2+hibernate2对大字段的处理实例

    在处理大数据字段时,通常涉及到的是文本、图片或者其他大型二进制数据,这些数据不适合直接存储在数据库的普通字段中,因为这可能会导致性能下降,甚至可能超出数据库字段的限制。在Spring1.2和Hibernate2的环境下...

    Spring4+SpringMVC4+Hibernate4整合源码

    同时,Hibernate4还增强了对大数据和分布式系统的支持,如Cassandra和NoSQL数据库的集成。 4. **整合**:将这三个框架整合在一起,可以构建出一个完整的企业级应用。Spring4作为整体的容器管理SpringMVC和Hibernate...

    hibernate动态分表

    总的来说,Hibernate动态分表是一种应对大数据场景的有效手段,通过合理的设计和配置,能够大大提高系统的可扩展性和性能。在实际开发中,结合源码分析,我们可以更深入地理解其工作机制,以便更好地运用到项目中。

    C#Hibernate数据库海量读写快速存储

    在IT行业中,数据库...总结来说,结合C#、Hibernate和SQL Server,开发者可以构建高效的数据处理系统,尤其是处理海量数据时。通过优化代码、数据库设计和服务器配置,可以显著提升读写性能,满足大规模业务的需求。

    hibernate保存blob,clob对象

    在Java的持久化框架Hibernate中,处理大数据类型如BLOB(Binary Large Object)和CLOB(Character Large Object)是一项常见的任务。BLOB用于存储二进制数据,如图片、视频或文档,而CLOB则用于存储大文本数据,如...

    hibernate 3.2中文手册 中文文档

    - 讨论如何处理二进制大数据对象(BLOBs)。 - **3.4.4 二级缓存与查询缓存** - 介绍Hibernate的缓存机制,包括二级缓存和查询缓存的使用。 - **3.4.5 查询语言中的替换** - 讲解如何使用HQL或QBC进行查询替换...

    Hibernate操作数据库大字段的示例(附源码及详细开发使用方法)。

    当处理大数据字段,如长文本、图片或视频等时,Hibernate提供了有效的方法来存储和检索这些数据。本示例将详细介绍如何在Hibernate中操作数据库的大字段。 首先,我们来理解“大字段”在数据库中的概念。在SQL标准...

    spring1.2+hibernate3对大字段的处理实例,供大家学习与交流,可直接运行

    4. **BLOB/CLOB处理**:在Hibernate中,BLOB用于存储二进制大数据,CLOB用于存储字符大数据。在配置实体类时,可以指定对应的字段为这些类型。 5. **批处理**:在进行大量大字段操作时,可以使用批处理更新或插入,...

    hibernate-search-5.6.0.Alpha3-dist.zip

    总结来说,Hibernate Search 5.6.0.Alpha3是一个强大的企业级搜索解决方案,它结合了ORM的强大功能和搜索引擎的高效检索,为开发者提供了在Java应用程序中实现复杂、高性能搜索的途径。无论是小规模的个人项目还是大...

Global site tag (gtag.js) - Google Analytics