hibernate大数据性能处理

 我来答
一袭可爱风1718
2022-09-30 · TA获得超过1.2万个赞
知道大有可为答主
回答量:6265
采纳率:99%
帮助的人:34.5万
展开全部
    在项目中使用Hibernate进行大数据量的性能测试 有一些总结     ) 在处理大数据量时 会有大量的数据缓冲保存在Session的一级缓存中 这缓存大太时会严重显示性能 所以在使用Hibernate处理大数据量的 可以使用session clear()或者session Evict(Object) 在处理过程中 清除全部的缓存或者清除某个对象     ) 对大数据量查询时 慎用list()或者iterator()返回查询结果     使用List()返回结果时 Hibernate会所有查询结果初始化为持久化对象 结果集较大时 会占用很多的处理时间     而使用iterator()返回结果时 在每次调用iterator next()返回对象并使用对象时 Hibernate才调用查询将对应的对象初始化 对于大数据量时 每调用一次查询都会花费较多的时间 当结果集较大 但是含有较大量相同的数据 或者结果集不是全部都会使用时 使用iterator()才有优势     对于大数据量 使用qry scroll()可以得到较好的处理速度以及性能 而且直接对结果集向前向后滚动     ) 对于关联操作 Hibernate虽然可以表达复杂的数据关系 但请慎用 使数据关系较为简单时会得到较好的效率 特别是较深层次的关联时 性能会很差     ) 对含有关联的PO(持久化对象)时 若default cascade= all 或者 save update 新增PO时 请注意对PO中的集合的赋值操作 因为有可能使得多执行一次update操作     ) 在一对多 多对一的关系中 使用延迟加载机制 会使不少的对象在使用时方会初始化 这样可使得节省内存空间以及减少的负荷 而且若PO中的集合没有被使用时 就可减少互数据库的交互从而减少处理时间   数据库    什么叫n+ 次select查询问题?    在Session的缓存中存放的是相互关联的对象图 默认情况下 当Hibernate从数据库中加载Customer对象时 会同时加载所有关联的Order对象 以Customer和Order类为例 假定ORDERS表的CUSTOMER_ID外键允许为null 图 列出了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= ;    select * from ORDERS where CUSTOMER_ID= ;    select * from ORDERS where CUSTOMER_ID= ;    select * from ORDERS where CUSTOMER_ID= ;    通过以上 条select语句 Hibernate最后加载了 个Customer对象和 个Order对象 在内存中形成了一幅关联的对象图 参见图     Hibernate在检索与Customer关联的Order对象时 使用了默认的立即检索策略 这种检索策略存在两大不足     (a) select语句的数目太多 需要频繁的访问数据库 会影响检索性能 如果需要查询n个Customer对象 那么必须执行n+ 次select查询语句 这就是经典的n+ 次select查询问题 这种检索策略没有利用SQL的连接查询功能 例如以上 条select语句完全可以通过以下 条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语句的数目     刚查阅了hibernate 的文档     查询抓取(默认的)在N+ 查询的情况下是极其脆弱的 因此我们可能会要求在映射文档中定义使用连接抓取     <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)语句     ) 对于大数据量新增 修改 删除操作或者是对大数据量的查询 与数据库的交互次数是决定处理时间的最重要因素 减少交互的次数是提升效率的最好途径 所以在开发过程中 请将show_sql设置为true 深入了解Hibernate的处理过程 尝试不同的方式 可以使得效率提升     ) Hibernate是以JDBC为基础 但是Hibernate是对JDBC的优化 其中使用Hibernate的缓冲机制会使性能提升 如使用二级缓存以及查询缓存 若命中率较高明 性能会是到大幅提升     ) Hibernate可以通过设置hibernate jdbc fetch_size hibernate jdbc batch_size等属性 对Hibernate进行优化     hibernate jdbc fetch_size     hibernate jdbc batch_size     这两个选项非常非常非常重要!!!将严重影响Hibernate的CRUD性能!    C = create R = read U = update D = delete    Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数

    例如一次查询 万条记录 对于Oracle的JDBC驱动来说 是不会 次性把 万条取出来的 而只会取出Fetch Size条数 当纪录集遍历完了这些记录以后 再去数据库取Fetch Size条数据     因此大大节省了无谓的内存消耗 当然Fetch Size设的越大 读数据库的次数越少 速度越快 Fetch Size越小 读数据库的次数越多 速度越慢     这有点像平时我们写程序写硬盘文件一样 设立一个Buffer 每次写入Buffer 等Buffer满了以后 一次写入硬盘 道理相同     Oracle数据库的JDBC驱动默认的Fetch Size= 是一个非常保守的设定 根据我的测试 当Fetch Size= 的时候 性能会提升 倍之多 当Fetch Size= 性能还能继续提升 % Fetch Size继续增大 性能提升的就不显著了     因此我建议使用Oracle的一定要将Fetch Size设到     不过并不是所有的数据库都支持Fetch Size特性 例如MySQL就不支持     MySQL就像我上面说的那种最坏的情况 他总是一下就把 万条记录完全取出来 内存消耗会非常非常惊人!这个情况就没有什么好办法了 :(    Batch Size是设定对数据库进行批量删除 批量更新和批量插入的时候的批次大小 有点相当于设置Buffer缓冲区大小的意思     Batch Size越大 批量操作的向数据库发送sql的次数越少 速度就越快 我做的一个测试结果是当Batch Size= 的时候 使用Hibernate对Oracle数据库删除 万条记录需要 秒 Batch Size = 的时候 删除仅仅需要 秒!!!    //    我们通常不会直接操作一个对象的标识符(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 设置外连接抓取树的最大深度    取值 建议设置为 到 之间    就是每次你在查询时 会级联查询的深度 譬如你对关联vo设置了eager的话 如果fetch_depth值太小的话 会发多很多条sql    Hibernate的Reference之后 可以采用批量处理的方法 当插入的数据超过 时 就flush session并且clear     下面是一个测试method        /** */ /**          * 测试成批插入数据的事务处理 返回是否成功          *          *  @param objPO Object          *  @return boolean      */       public boolean  insertBatch( final  Object objPO)   {      boolean  isSuccess  = false ;             Transaction transaction  = null ;             Session session  = openSession()        try  {                 transaction  = session beginTransaction()        for  ( int  i  = ; i  < ; i ++ )   {                     session save(objPO)        if  (i  % == )   {      //  flush a batch of inserts and release memory                          session flush()                          session clear()                      }                 }                 mit()                  ( transaction wasCommitted:       + transaction wasCommitted())                  isSuccess  = true ;              } catch  (HibernateException ex)   {       if  (transaction  != null )   {       try  {                         transaction rollback()                          logger error( transaction wasRolledBack:       + transaction wasRolledBack())                       } catch  (HibernateException ex )   {                         logger error(ex getMessage())                          ex 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()                 ( good job! )             }    }    每做一次数据操作 就更新一次Session 这样可以保证每次数据操作都成功 否则就让Spring去控制它roll back吧     最后 记得关闭Session       Session session  =  openSession()     doBusiness(session)     session close()   // 关闭session lishixinzhi/Article/program/Java/hx/201311/26054

已赞过 已踩过<
你对这个回答的评价是?
评论 收起
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式