亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Hibernate優化_Hibernate性能優化_Hibernate優

系統 1541 0

Hibernate 是筆者使用了超過5年的優秀ORM框架,雖然說使用了5年,但筆者并沒有把握說自己真正意義上的精通Hibernate。說道熟悉Hibernate還差不多,因為Hibernate用法和特性只要使用過或許都很簡單,但是要做到發揮Hibernate最大限度的潛力, Hibernate優化 ,或者說 Hibernate性能優化 筆者僅僅是初窺門徑而已。這里摘錄一篇牛人對于 Hibernate優化 的文章,希望對自己以后的使用指引下方向吧。這篇是上一篇的續作,著實是不錯,推薦hibernate用戶,尤其是像我一樣的用戶看看,會有收獲的

4.6 HQL調優

4.6.1 索引調優

HQL看起來和SQL很相似。從HQL的WHERE子句中通常可以猜到相應的SQL WHERE子句。WHERE子句中的字段決定了數據庫將選擇的索引。

大多數Hibernate開發者所常犯的一個錯誤是無論何時,當需要新WHERE子句的時候都會創建一個新的索引。因為索引會帶來額外的數據更新開銷,所以應該爭取創建少量索引來覆蓋盡可能多的查詢。
4.1節 讓你使用一個集合來處理所有可能的數據搜索條件。如果這不太實際,那么你可以使用后端剖析工具來創建一個針對應用程序涉及的所有 SQL的集合。基于那些搜索條件的分類,你最終會得到一個小的索引集。與此同時,還可以嘗試向WHERE子句中添加額外的謂語來匹配其他WHERE子句。

范例7

有兩個UI搜索器和一個后端守護進程搜索器來搜索名為iso_deals的表。第一個UI搜索器在unexpectedFlag、dealStatus、tradeDate和isold屬性上有謂語。

第二個UI搜索器基于用戶鍵入的過濾器,其中包括的內容除tradeDate和isold以外還有其他屬性。開始時所有這些過濾器屬性都是可選的。
后端搜索器基于isold、participantCode和transactionType屬性。
經過進一步業務分析,發現第二個UI搜索器實際是基于一些隱式的unexpectedFlag和dealStatus值來選擇數據的。我們還讓tradeDate成為過濾器的必要屬性(為了使用數據庫索引,每個搜索過濾器都應該有必要屬性)。

鑒于這一點,我們依次使用unexpectedFlag、dealStatus、tradeDate和isold構造了一個復合索引。兩個UI搜索 器都能共用它。(順序很重要,如果你的謂語以不同的順序指定這些屬性或在它們前羅列了其他屬性,數據庫就不會選擇該復合索引。)

后端搜索器和UI搜索器區別太大,因此我們不得不為它構造另一個復合索引,依次使用isold、participantCode和transactionType。

4.6.2綁定參數 vs.字符串拼接

既可以使用綁定參數構造HQL的WHERE子句,也可以使用字符串拼接的方法,該決定對性能會有一定影響。使用綁定參數的原因是讓數據庫一次解析 SQL,對后續的重復請求復用生成好的執行計劃,這樣做節省了CPU時間和內存。然而,為達到最優的數據訪問效率,不同的綁定值可能需要不同的SQL執行 計劃。

例如,一小段數據范圍可能只返回數據總量的5%,而一大段數據范圍可能返回數據總量的90%。前者使用索引更好,而后者則最好使用全表掃描。

建議OLTP使用綁定參數,數據倉庫使用字符串拼接,因為OLTP通常在一個事務中重復插入和更新數據,只取少量數據;數據倉庫通常只有少量SQL查詢,有一個確定的執行計劃比節省CPU時間和內存更為重要。

要是你知道你的OLTP搜索對不同綁定值應該使用相同執行計劃又該怎么辦呢?

Oracle 9i及以后版本在第一次調用綁定參數并生成執行計劃時能探出參數值。后續調用不會再探測,而是重用之前的執行計劃。

4.6.3聚合及排序

你可以在數據庫中進行聚合和“order by”,也可以在應用程序的服務層中事先加載所有數據然后做聚合和“order by”操作。推薦使用前者,因為數據庫在這方面通常會比你的應用程序做得好。此外,這樣做還能節省網絡帶寬,這也是一種擁有跨數據庫移植性的做法。

當你的應用程序對數據聚合和排序有HQL不支持的特定業務規則時除外。

4.6.4覆蓋抓取策略

詳見 4.7.1節

4.6.5本地查詢

本地查詢調優其實并不直接與HQL有關。但HQL的確可以讓你直接向底層數據庫傳遞本地查詢。我們并不建議這么做,因為本地查詢在數據庫間不可移植。

4.7抓取策略調優

抓取策略決定了在應用程序需要訪問關聯對象時,Hibernate以何種方式以及何時獲取關聯對象。HRD中的 第20章“改善性能” 對該主題作了很好的闡述,我們在此將關注它的使用方法。

4.7.1覆蓋抓取策略

不同的用戶可能會有不同的數據抓取要求。Hibernate允許在兩個地方定義數據抓取策略,一處是在映射元數據中,另一處是在HQL或Criteria中覆蓋它。

常見的做法是基于主要的抓取用例在映射元數據中定義默認抓取策略,針對少數用例在HQL和Criteria中覆蓋抓取策略。

假設pojoA和pojoB是父子關系實例。如果根據業務規則,只是偶爾需要從實體兩端加載數據,那你可以聲明一個延遲加載集合或代理抓取 (proxy fetching)。當你需要從實體兩端獲取數據時,可以用立即抓取(eager fetching)覆蓋默認策略,例如使用HQL或Criteria配置連接抓取(join fetching)。

另一方面,如果業務規則在大多數時候需要從實體兩端加載數據,那么你可以聲明立即抓取并在Criteria中設置延遲加載集合或代理抓取來覆蓋它(HQL目前還不支持這樣的覆蓋)。

4.7.2 N+1模式或是反模式?

select抓取會導致N+1問題。如果你知道自己總是需要從關聯中加載數據,那么就該始終使用連接抓取。在下面兩個場景中,你可能會把N+1視為一種模式而非反模式。

第一種場景,你不知道用戶是否會訪問關聯對象。如果他/她沒有訪問,那么你贏了;否則你仍然需要額外的N次select SQL語句。這是一種令人左右為難的局面。

第二種場景,pojoA和很多其他POJO有one-to-many關聯,例如pojoB和pojoC。使用立即的內連接或外連接抓取會在結果集中 將pojoA重復很多次。當pojoA中有很多非空屬性時,你不得不將大量數據加載到持久層中。這種加載需要很多時間,既有網絡帶寬的原因,如果 Hibernate的會話是有狀態的,其中也會有會話緩存的原因(內存消耗和GC暫停)。

如果你有一個很長的one-to-many關聯鏈,例如從pojoA到pojoB到pojoC以此類推,情況也是類似的。

你也許會去使用HQL中的DISTINCT關鍵字或Cirteria中的distinct功能或是 Java 的Set接口來消除重復數據。但所有這些都是在Hibernate(在持久層)中實現的,而非數據庫中。

如果基于你的網絡和內存配置的測試表明N+1性能更好,那么你可以使用批量抓取、subselect抓取或二級緩存來做進一步調優。

范例8

以下是一個使用批量抓取的HBM文件片段:

      <
      
        class
      
      
        name
      
      =
      
        "
        
          pojoA
        
        " 
      
      
        table
      
      =
      
        "
        
          pojoA
        
        "
      
      >
…
<
      
        set
      
      
        name
      
      =
      
        "
        
          pojoBs
        
        "
      
      
        fetch
      
      =
      
        "
        
          select
        
        "
      
      
        batch-size
      
      =
      
        "
        
          10
        
        "
      
      >

<
      
        key
      
      
        column
      
      =
      
        "
        
          pojoa_id
        
        "
      
      />
…
</
      
        set
      
      >
</
      
        class
      
      >

    

以下是多端pojoB生成的SQL:

      
        
          select
        
      
        
          from
        
      
       pojoB 
      
        
          where
        
      
       pojoa_id 
      
        
          in
        
      
      (?,?,?,?,?, ?,?,?,?,?);
    

問號數量與batch-size值相等。因此N次額外的關于pojoB的select SQL語句被減少到了N/10次。

如果將 fetch = select 替換成 fetch = subselect ,pojoB生成的SQL語句就是這樣的:

      
        
          select
        
      
        
          from
        
      
       pojoB 
      
        
          where
        
      
       pojoa_id 
      
        
          in
        
      
      (
      
        
          select
        
      
       id 
      
        
          from
        
      
       pojoA 
      
        
          where
        
      
       …);
    

盡管N次額外的select減少到1次,但這只在重復運行pojoA的查詢開銷很低時才有好處。

如果pojoA中的pojoB集合很穩定,或pojoB有pojoA的many-to-one關聯,而且pojoA是只讀引用數據,那么你可以使用二級緩存來緩存pojoA以消除N+1問題( 4.8.1節 中有一個例子)。

4.7.3延遲屬性抓取

除非有一張擁有很多你不需要的字段的遺留表,否則不應該使用這種抓取策略,因為它的延遲屬性分組會帶來額外的SQL。

在業務分析和設計過程中,你應該將不同數據獲取或修改分組放到不同的領域對象實體中,而不是使用這種抓取策略。

如果不能重新設計遺留表,可以使用HQL或Criteria提供的投影功能來獲取數據。

4.8 二級緩存調優

HRD 第20.2節 “二級緩存” 中的描述對大多數開發者來說過于簡單,無法做出選擇。3.3版及以后版本不再推薦使用基于“CacheProvider”的緩存,而用基于“RegionFactory”的緩存,這也讓人更糊涂了。但是就算是最新的3.5參考文檔也沒有提及如何使用新緩存方法。

出于下述考慮,我們將繼續關注于老方法:

4.8.1 基于CacheProvider的緩存機制

理解該機制是做出合理選擇的關鍵。關鍵的類/接口是CacheConcurrencyStrategy和它針對4中不同緩存使用的實現類,還有EntityUpdate/Delete/InsertAction。

針對并發緩存訪問,有三種實現模式:

  • 針對“read-only”的只讀模式。
  • 無論是鎖還是事務都沒影響,因為緩存自數據從數據庫加載后就不會改變。

  • 針對“read-write”和“nonstrict-read-write”的非事務感知(non-transaction-aware)讀寫模式。
  • 對緩存的更新發生在數據庫事務完成后。緩存需要支持鎖。

  • 針對“transactional”的事務感知讀寫。
  • 對緩存和數據庫的更新被包裝在同一個JTA事務中,這樣緩存與數據庫總是保持同步的。數據庫和緩存都必須支持JTA。盡管緩存事務內部依賴于緩存鎖,但Hibernate不會顯式調用任何的緩存鎖函數。

以數據庫更新為例。EntityUpdateAction對于事務感知讀寫、“read-write”的非事務感知讀寫,還有“nonstrict-read-write”的非事務感知讀寫相應有如下調用序列:

  • 在一個JTA事務中更新數據庫;在同一個事務中更新緩存。
  • 軟鎖緩存;在一個事務中更新數據庫;在上一個事務成功完成后更新緩存;否則釋放軟鎖。
  • 軟鎖只是一種特定的緩存值失效表述方式,在它獲得新數據庫值前阻止其他事務讀寫緩存。那些事務會轉而直接讀取數據庫。

    緩存必須支持鎖;事務支持則不是必須的。如果緩存是一個集群,“更新緩存”的調用會將新值推送給所有副本,這通常被稱為“推(push)”更新策略。

  • 在一個事務中更新數據庫;在上一個事務完成前就清除緩存;為了安全起見,無論事務成功與否,在事務完成后再次清除緩存。
  • 既不需要支持緩存鎖,也不需要支持事務。如果是緩存集群,“清除緩存”調用會讓所有副本都失效,這通常被稱為“拉(pull)”更新策略。

對于實體的刪除或插入動作,或者集合變更,調用序列都是相似的。

實際上,最后兩個異步調用序列仍能保證數據庫和緩存的一致性(基本就是“read committed”的隔離了級別),這要歸功于第二個序列中的軟鎖和“更新數據庫”后的“更新緩存”,還有最后一個調用序列中的悲觀“清除緩存”。

基于上述分析,我們的建議是:

  • 如果數據是只讀的,例如引用數據,那么總是使用“read-only”策略,因為它是最簡單、最高效的策略,也是集群安全的策略。
  • 除非你真的想將緩存更新和數據庫更新放在一個JTA事務里,否則不要使用“transactional”策略,因為JTA需要漫長的兩階段提交處理,這導致它基本是性能最差的策略。
  • 依筆者看來,二級緩存并非一級數據源,因此使用JTA也未必合理。實際上最后兩個調用序列在大多數場景下是個不錯的替代方案,這要歸功于它們的數據一致性保障。

  • 如果你的數據讀很多或者很少有并發緩存訪問和更新,那么可以使用“nonstrict-read-write”策略。感謝它的輕量級“拉”更新策略,它通常是性能第二好的策略。
  • 如果你的數據是又讀又寫的,那么使用“read-write”策略。這通常是性能倒數第二的策略,因為它要求有緩存鎖,緩存集群中使用重量級的“推”更新策略。

范例9

以下是一個ISO收費類型的HBM文件片段:

      <
      
        class
      
      
        name
      
      =
      
        "
        
          IsoChargeType
        
        ">
      
      
   <
      
        property
      
      
        name
      
      =
      
        "
        
          isoId
        
        "
      
      
        column
      
      =
      
        "
        
          ISO_ID
        
        "
      
      
        not-null
      
      =
      
        "
        
          true
        
        "
      
      />

   <
      
        many-to-one
      
      
        name
      
      =
      
        "
        
          estimateMethod
        
        "
      
      
        fetch
      
      =
      
        "
        
          join
        
        "
      
      
        lazy
      
      =
      
        "
        
          false
        
        "
      
      />

   <
      
        many-to-one
      
      
        name
      
      =
      
        "
        
          allocationMethod
        
        "
      
      
        fetch
      
      =
      
        "
        
          join
        
        "
      
      
        lazy
      
      =
      
        "
        
          false
        
        "
      
      />

   <
      
        many-to-one
      
      
        name
      
      =
      
        "
        
          chargeTypeCategory
        
        "
      
      
        fetch
      
      =
      
        "
        
          join
        
        "
      
      
        lazy
      
      =
      
        "
        
          false
        
        "
      
      />

</
      
        class
      
      >
    

一些用戶只需要ISO收費類型本身;一些用戶既需要ISO收費類型,還需要它的三個關聯對象。簡單起見,開發者會立即加載所有三個關聯對象。如果項目中沒人負責Hibernate調優,這是很常見的。

4.7.1節 中講過了最好的方法。因為所有的關聯對象都是只讀引用數據,另一種方法是使用延遲抓取,打開這些對象的二級緩存以避免N+1問題。實際上前一種方法也能從引用數據緩存中獲益。

因為大多數項目都有很多被其他數據引用的只讀引用數據,上述兩種方法都能改善全局系統性能。

4.8.2 RegionFactory

下表是新老兩種方法中對應的主要類/接口:

新方法 老方法
RegionFactory CacheProvider
Region Cache
EntityRegionAccessStrategy CacheConcurrencyStrategy
CollectionRegionAccessStrategy CacheConcurrencyStrategy

第一個改進是RegionFactory構建了特定的Region,例如EntityRegion和TransactionRegion,而不是使 用一個通用的訪問Region。第二個改進是對于特定緩存的“usage”屬性值,Region要求構建自己的訪問策略,而不是所有Region都一直使 用CacheConcurrencyStrategy的4種實現。

要使用新方法,應該設置factory_class而非provider_class配置屬性。以Ehcache 2.0為例:

    <property name="hibernate.cache.region.factory_class">
        net.sf.ehcache.hibernate.EhCacheRegionFactory
</property>
  

其他相關的Hibernate緩存配置都和老方法一樣。

新方法也能向后兼容遺留方法。如果還是只配了CacheProvider,新方法中將使用下列自說明(self-explanatory)適配器和橋隱式地調用老的接口/類:

RegionFactoryCacheProviderBridge、EntityRegionAdapter、 CollectionRegionAdapter、QueryResultsRegionAdapter、 EntityAccessStrategyAdapter和CollectionAccessStrategyAdapter

4.8.3 查詢緩存

二級緩存也能緩存查詢結果。如果查詢開銷很大而且要重復運行,這也會很有幫助。

4.9批量處理調優

大多數Hibernate的功能都很適合那些每個事務都通常只處理少量數據的OLTP系統。但是,如果你有一個數據倉庫或者事務需要處理大量數據,那么就另當別論了。

4.9.1使用有狀態會話的非DML風格批處理

如果你已經在使用常規會話了,那這是最自然的方法。你需要做三件事:

  • 配置下列3個屬性以開啟批處理特性:
      hibernate.jdbc.batch_size 30
  hibernate.jdbc.batch_versioned_data true
  hibernate.cache.use_second_level_cache false
  

batch_size設置為正值會開啟JDBC2的批量更新,Hibernate的建議值是5到30。基于我們的測試,極低值和極高值性能都很差。只要取值在合理范圍內,區別就只有幾秒而已。如果網絡夠快,這個結果是一定的。

第二個配置設為true,這要求JDBC驅動在executeBatch()方法中返回正確的行數。對于Oracle用戶而言,批量更新時不能將其設為true。請閱讀Oracle的《JDBC Developer’s Guide and Reference》中的“ 標準批處理的Oracle實現中的更新計數 ”( Update Counts in the Oracle Implementation of Standard Batching )以獲得更多詳細信息。因為它對批量插入來說還是安全的,所以你可以為批量插入創建單獨的專用數據源。最后一個配置項是可選的,因為你可以在會話中顯式關閉二級緩存。

  • 像如下范例中那樣定期刷新(flush)并清除一級會話緩存:
  •      Session session = sessionFactory.openSession();
     Transaction tx = session.beginTransaction();
    
     for ( int i=0; i<100000; i++ ) {
         Customer customer = new Customer(.....);
         //if your hibernate.cache.use_second_level_cache is true, call the following:
         session.setCacheMode(CacheMode.IGNORE);
         session.save(customer);
         if (i % 50 == 0) { //50, same as the JDBC batch size
         //flush a batch of inserts and release memory:
         session.flush();
         session.clear();
         }
     }
     tx.commit();
     session.close();
    
      

    批處理通常不需要數據緩存,否則你會將內存耗盡并大量增加GC開銷。如果內存有限,那這種情況會很明顯。

  • 總是將批量插入嵌套在事務中。
  • 每次事務修改的對象數量越少就意味著會有更多數據庫提交,正如 4.5節 所述每次提交都會帶來磁盤相關的開銷。

    另一方面,每次事務修改的對象數量越多就意味著鎖定變更時間越長,同時數據庫需要更大的redo log。

    4.9.2使用無狀態會話的非DML風格批處理

    無狀態會話執行起來比上一種方法更好,因為它只是JDBC的簡單包裝,而且可以繞開很多常規會話要求的操作。例如,它不需要會話緩存,也不和任何二級緩存或查詢緩存有交互。
    然而它的用法并不簡單。尤其是它的操作并不會級聯到所關聯的實例上;你必須自己來處理它們。

    4.9.3 DML風格

    使用DML風格的插入、更新或刪除,你直接在數據庫中操作數據,這和前兩種方法在Hibernate中操作數據的情況有所不同。

    因為一個DML風格的更新或刪除相當于前兩種方法中的多個單獨的更新或刪除,所以如果更新或刪除中的WHERE子句暗示了恰當的數據庫索引,那么使用DML風格的操作能節省網絡開銷,執行得更好。

    強烈建議結合使用DML風格操作和無狀態會話。如果使用有狀態會話,不要忘記在執行DML前清除緩存,否則Hibernate將會更新或清除相關緩存(見下面的范例10)。

    4.9.4批量加載

    如果你的HQL或Criteria會返回很多數據,那么要注意兩件事:

    • 用下列配置開啟批量抓取特性:
        hibernate.jdbc.fetch_size 10
      

    fetch_size設置為正值將開啟JDBC批量抓取特性。相對快速網絡,在慢速網絡中這一點更為重要。Oracle建議的經驗值是10。你應該基于自己的環境進行測試。

  • 在使用上述任一方法時都要關閉緩存,因為批量加載一般是一次性任務。受限于內存容量,向緩存中加載大量數據通常也意味著它們很快會被清除出去,這會增加GC開銷。
  • 范例10

    我們有一個后臺任務,分段加載大量的IsoDeal數據用于后續處理。我們還會在分段數據交給下游系統處理前將其更新為處理中狀態。最大的一段有50萬行數據。以下是原始代碼中截取出來的一段:

          Query query = session.createQuery("
          
            FROM IsoDeal d WHERE chunk-clause
          
          ");
    query.setLockMode(
          
            "d"
          
          , LockMode.
          
            
              UPGRADE
            
          
          ); //for Inprocess status update
    List<IsoDeal> isoDeals = query.list();
    for (IsoDeal isoDeal : isoDeals) { //update status to Inprocess
       isoDeal.setStatus
          
            ("Inprocess"
          
          );
    }
    return isoDeals;
        

    包含上述代碼的方法加上了Spring 2.5聲明式事務的注解。加載并更新50萬行數據大約花了10分鐘。我們識別出了以下這些問題:

    • 由于會話緩存和二級緩存的原因,系統會頻繁地內存溢出。
    • 就算沒有內存溢出,當內存消耗很高時GC的開銷也會很大。
    • 我們還未設置fetch_size。
    • 就算我們設置了batch_size,for循環也創建了太多update SQL語句。

    不幸的是Spring 2.5不支持Hibernate無狀態會話,所以我們只能關閉二級緩存;設置fetch_size;用DML風格的更新來代替for循環,以此改善性能。

    但是,執行時間還是要6分鐘。將Hibernate的日志級別調成trace后,我們發現是更新會話緩存造成了延時。通過在DML更新前清除會話緩存,我們將時間縮短到了4分鐘,全部都是將數據加載到會話緩存中花費的時間。

    4.10 SQL生成調優

    本節將向你展示如何減少SQL生成的數量。

    4.10.1 N+1抓取問題

    “select抓取”策略會導致N+1問題。如果“連接抓取”策略適合你的話,你應該始終使用該策略避免N+1問題。

    但是,如果“連接抓取”策略執行效果不理想,就像 4.7.2節 中那樣,你可以使用“subselect抓取”、“批量抓取”或“延遲集合抓取”來減少所需的額外SQL語句數。

    4.10.2 Insert+Update問題

    范例11

    我們的ElectricityDeal與DealCharge有單向one-to-many關聯,如下列HBM文件片段所示:

          <
          
            class
          
          
            name
          
          =
          
            "
            
              ElectricityDeal
            
            "
          
          
            select-before-update
          
          =
          
            "
            
              true
            
            "
          
          
            dynamic-update
          
          =
          
            "
            
              true
            
            "
          
          
            dynamic-insert
          
          =
          
            "
            
              true
            
            "
          
          >
        
          
            <id
          
          
            name
          
          =
          
            "
            
              key
            
            "
          
          
            column
          
          =
          
            "
            
              ID
            
            "
          
          >
    
            
          
            <generator
          
          
            class
          
          =
          
            "
            
              sequence
            
            "
          
          >
                
          
            <param
          
          
            name
          
          =
          
            "
            
              sequence
            
            "
          
          >SEQ_ELECTRICITY_DEALS<
          
            /param
          
          >
    
            
          
            </generator>
          
          
            </id>
        …
        <set
          
          
            name
          
          =
          
            "
            
              dealCharges
            
            " 
          
          
            cascade
          
          =
          
            "
            
              all-delete-orphan
            
            ">
          
          
            <key 
          
          
            column
          
          =
          
            "
            
              DEAL_KEY
            
            "
          
          
            not-null
          
          =
          
            "
            
              false
            
            "
          
          
            update
          
          =
          
            "
            
              true
            
            "
          
          
            on-delete
          
          =
          
            "
            
              noaction
            
            "
          
          />
            
          
            <one-to-many
          
          
            class
          
          =
          
            "
            
              DealCharge
            
            "
          
          />
        
          
            </set>  </class>
          
        

    在“key”元素中,“not-null”和“update”對應的默認值是false和true,上述代碼為了明確這些取值,將它們寫了出來。

    如果你想創建一個ElectricityDeal和十個DealCharge,會生成如下SQL語句:

    • 1句ElectricityDeal的插入語句;
    • 10句DealCharge的插入語句,其中不包括外鍵“DEAL_KEY”;
    • 10句DealCharge字段“DEAL_KEY”的更新語句。

    為了消除那額外的10句更新語句,可以在那10句DealCharge插入語句中包含“DEAL_KEY”,你需要將“not-null”和“update”分別修改為true和false。

    另一種做法是使用雙向或many-to-one關聯,讓DealCharge來管理關聯。

    4.10.3 更新前執行select

    在范例11中,我們為ElectricityDeal加上了select-before-update,這會對瞬時(transient)對象或分離(detached)對象產生額外的select語句,但卻能避免不必要的數據庫更新。

    你應該做出一些權衡,如果對象沒多少屬性,不需要防止不必要的數據庫更新,那么就不要使用該特性,因為你那些有限的數據既沒有太多網絡傳輸開銷,也不會帶來太多數據庫更新開銷。

    如果對象的屬性較多,例如是一張大的遺留表,那你應該開啟該特性,和“dynamic-update”結合使用以避免太多數據庫更新開銷。

    4.10.4 級聯刪除

    在范例11中,如果你想刪除1個ElectricityDeal和它的100個DealCharge,Hibernate會對DealCharge做100次刪除。

    如果將“on-delete”修改為“cascade”,Hibernate不會執行DealCharge的刪除動作;而是讓數據庫根據ON CASCADE DELETE約束自動刪除那100個DealCharge。不過,需要讓DBA開啟ON CASCADE DELETE約束,大多數DBA不愿意這么做,因為他們想避免父對象的意外刪除級聯到它的依賴對象上。此外,還要注意,該特性會繞過Hibernate對 版本數據(versioned data)的常用樂觀鎖策略。

    4.10.5 增強的序列標識符生成器

    范例11中使用Oracle的序列作為標識符生成器。假設我們保存100個ElectricityDeal,Hibernate會將下面的SQL語句執行100次來獲取下一個可用的標識符:

        
          
            select
          
        
         SEQ_ELECTRICITY_DEALS.NEXTVAL 
        
          
            from
          
        
         dual;
      

    如果網絡不是很快,那這無疑會降低效率。3.2.3及后續版本中增加了一個增強的生成器“SequenceStyleGenerator”,它帶了兩個優化器:hilo和pooled。盡管HRD的 第5章“基礎O/R映射” 講到了這兩個優化器,不過內容有限。兩個優化器都使用了HiLo算法,該算法生成的標識符等于Hi值加上Lo值,其中Hi值代表組號,Lo值順序且重復地從1迭代到最大組大小,組號在Lo值“轉回到”1時加1。

    假設組大小是5(可以用max_lo或increment_size參數來表示),下面是個例子:

    hibernate preformance3 300x33 Hibernate優化 Hibernate性能優化 Hibernate優化方案(下)

    hibernate性能優化

    • hilo優化器
    • 組號取自數據庫序列的下一個可用值,Hi值由Hibernate定義,是組號乘以increment_size參數值。

    • pooled優化器
    • Hi值直接取自數據庫序列的下一個可用值。數據庫序列的增量應該設置為increment_size參數值。

    直到內存組中的值耗盡后,兩個優化器才會去訪問數據庫,上面的例子每5個標識值符訪問一次數據庫。使用hilo優化器時,你的序列不能再被其他應用程序使用,除非它們使用與Hibernate相同的邏輯。使用pooled優化器,在其他應用程序使用同一序列時則相當安全。

    兩個優化器都有一個問題,如果Hibernate崩潰,當前組內的一些標識符值就會丟失,然而大多數應用程序都不要求擁有連續的標識符值(如果你的數據庫,比方說Oracle,緩存了序列值,當它崩潰時你也會丟失標識符值)。

    如果在范例11中使用pooled優化器,新的id配置如下:

        
          <id
        
        
          name
        
        =
        
          "key"
        
        
          column
        
        =
        
          "
          
            ID"
          
        
        
          >
        
        
          <generator
        
        
          class
        
        =
        
          
            "org.hibernate.id.enhance
          
          .
          
            SequenceStyleGenerator"
          
          >
    
        
        
          <param
        
        
          name
        
        =
        
          
            "sequence_name"
          
          >
        
        SEQ_ELECTRICITY_DEALS
        
          </param>
        
        
          <param
        
        
          name
        
        =
        
          
            "initial_value"
          
          >
        
        0
        
          </param>
        
        
          <param
        
        
          name
        
        =
        
          
            "increment_size"
          
          >
        
        100
        
          </param>
        
        
          <param
        
        
          name
        
        =
        
          
            "optimizer "
          
          >
        
        pooled
        
          </param>
        
        
          </generator>
        
        
          </id>
        
      

    5 總結

    本文涵蓋了大多數你在Hibernate應用程序調優時會覺得很有用的調優技巧,其中的大多數時間都在討論那些行之有效卻缺乏文檔的調優主題,例如繼承映射、二級緩存和增強的序列標識符生成器。

    它還提到了一些Hibernate調優所必需的數據庫知識。一些范例中包含了你可能遇到的問題的實際解決方案。

    除此之外,值得一提的是Hibernate也可以和In-Memory Data Grid(IMDG)一起使用,例如Oracle的Coherance或GigaSpaces IMDG,這能讓你的應用程序達到毫秒級別。

    6 資源

    [1] Latest Hibernate Reference Documentation on jboss.com

    [2] Oracle 9i Performance Tuning Guide and Reference

    [3] Performance Engineering on Wikipedia

    [4] Program Optimization on Wikipedia

    [5] Pareto Principle (the 80/20 rule) on Wikipedia

    [6] Premature Optimization on acm.org

    [7] Java Performance Tuning by Jack Shirazi

    [8] The Law of Leaky Abstractions by Joel Spolsky

    [9] Hibernate’s StatisticsService Mbean configuration with Spring

    [10] JProbe by Quest Software

    [11] Java VisualVM

    [12] Column-oriented DBMS on Wikipedia

    [13] Apache DBCP BasicDataSource

    [14] JDBC Connection Pool by Oracle

    [15] Connection Failover by Oracle

    [16] Last Resource Commit Optimization (LRCO)

    [17] GigaSpaces for Hibernate ORM Users

    關于作者

    Yongjun Jiao 是SunGard Consulting Services的技術主管。過去10年中他一直是專業軟件開發者,他的專長包括Java SE、Java EE、Oracle和應用程序調優。他最近的關注點是高性能計算,包括內存數據網格、并行計算和網格計算。

    Stewart Clark 是SunGard Consulting Services的負責人。過去15年中他一直是專業軟件開發者和項目經理,他的專長包括Java核心編程、Oracle和能源交易。

    摘自: infoq
    英文地址:http://www.infoq.com/articles/hibernate_tuning

    Hibernate優化_Hibernate性能優化_Hibernate優化方案(下)


    更多文章、技術交流、商務合作、聯系博主

    微信掃碼或搜索:z360901061

    微信掃一掃加我為好友

    QQ號聯系: 360901061

    您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

    【本文對您有幫助就好】

    您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

    發表我的評論
    最新評論 總共0條評論
    主站蜘蛛池模板: 国产毛片在线看 | 综合免费一区二区三区 | 四虎影视成人永久在线观看 | 伊人天伊人天天网综合视频 | 97色综合| 久久精品播放 | 久久88 | 久久久精品久久 | 嫩草影院麻豆久久视频 | 色爱区综合五月激情 | 国产精品美女久久久久久 | 激情五月综合综合久久69 | 国产操美女 | 国产成人精品视频频 | 亚洲精品国产综合99久久一区 | 99精品国产费观看视频 | 奇米影视第四色7777 | 97高清国语自产拍 | 国产精品免费久久久免费 | 狠狠久久亚洲欧美专区 | 少妇美女极品美軳人人体 | 久久香蕉国产线看观看式 | 国产深夜福利19禁在线播放 | 国产精品久久免费 | 久久69精品久久久久久hb | 在线日本中文字幕 | 亚洲婷婷在线 | 精品视频中文字幕 | 日本α级毛片视频免费观看 | 激情 婷婷| 日产国产欧美视频一区精品 | 黄页网站 播放器 日本 | 亚洲精品国产手机 | 午夜视频在线观看国产www | 热久久99影院 | 国产亚洲欧美一区二区 | 99国产精品热久久久久久夜夜嗨 | 五月婷婷激情四射 | 99综合视频 | 久久com| 色青青草原桃花久久综合 |