88250

          Java

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            82 隨筆 :: 0 文章 :: 5 評論 :: 0 Trackbacks

          #

          B3log LogoGAE 博客 —— B3LOG Solo 0.2.0 正式發布了。

          該版本除了修復 Bugs,還增加了文件上傳,改進了緩存,以及加入了新皮膚 i-nove(移植自 iNove)。

          這個版本主要是 Bugs 修復及增強穩定性。另外,同步 Google Buzz 的殘次雞肋功能已經移除(只是注釋了界面顯示代碼 - -~)。

          新特性

          • 加入了文件上傳、下載
          • 加入了頁面評論
          • 加入了代碼高亮
          • 加入了緩存狀態查看
          • 加入了移除未使用標簽
          • 加入了頁面關鍵字、描述自定義

          Bug 修復

          • 修復了文章頁面緩存重復問題
          • 修復了博客配置偶爾會自動初始化問題
          • 修復了后臺評論過多時的顯示問題
          • 修復了站外相關閱讀顯示站內文章問題

          改進

          • 改進了博客地址配置的錯誤檢查
          • 改進了博客同步異常顯示
          • 改進了后臺管理評論時的數據加載

          其他

          • 移除了同步 Google Buzz 功能
          • 取消從 0.1.0 到 0.1.1 自動升級后臺任務
          • 加入了新皮膚——i-nove

          具體改動可以看這里

          升級

          如果您是 0.1.1 的用戶,那么請在部署完 0.2.0 后登錄后臺,訪問 http://${application-id}.appspot.com/upgrade/v011-v020.do 進行升級。

          改升級程序主要是對頁面評論特性進行數據一致性升級。

          項目

          如果在使用、測試中發現任何問題,如果您有任何意見或建議,請告知我們 :-)

          作者博客

          發布歷史

          1. GAE 博客——B3log Solo 0.1.1 發布了!
          2. B3log Solo 0.1.0 發布了!
          3. B3log Solo 0.1.0-preview2 發布了!
          4. Solo 0.1.0-Preview1 發布了

          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://88250.b3log.org/articles/2010/11/11/1289491357284.html
          posted @ 2010-11-12 00:03 88250 閱讀(122) | 評論 (0)編輯 收藏

          項目新聞

          NetBeans IDE 7.0 測試人員通緝中——注冊 NetCAT 7.0!

          image 你喜歡玩弄軟件并獵尋缺陷嗎?如果你是有經驗的 NetBeans IDE 用戶,并且從十一月到二月的每周都有幾小時空閑,就可以加為到 NetCAT 7.0 項目中的其他社區先鋒隊員里。讓你的 NetBeans Bug 得到修復并在其間結交朋友。請注冊 NetCAT 7.0!


          插件:PHP Nette 框架

          Ondrej Brejla 的 PHP Nette 框架插件提供了對 Nette 框架(一個用于快速簡便地創建質優且嶄新的 Web 應用程序的框架)的支持。該插件提供了對“Latte”模板語言的內建支持,帶有語法高亮和代碼完成。

          文章

          NetBeans、Drupal 插件和 Drupal 設置

          有關如何找到并安裝 Drupal NetBeans 插件,然后在 NetBeans IDE 中設置現有 Drupal 項目的概述。

          設置 NetBeans 以開發谷歌 Android 應用

          有關使用諸如 NetBeans IDE 的集成開發環境開發谷歌 Android 應用程序的快速入門文章。

          博客

          NetBeans IDE 7.0 M2 中的 PHP 支持有什么新點?

          該博文審閱了在 NetBeans IDE 7.0 第二個里程碑版本中的增強和新功能。

          NetBeans Maven:比 M2Eclipse 還好?

          Gunther Popp 親身嘗試了 NetBeans IDE 7.0 M2 的 Maven 3 支持,并發現了許多強大功能。

          使用 NetBeans 開發 GAE(Java)應用

          image B3LOG Solo 是一個使用 NetBeans IDE 開發的基于 Google App Engine(Java)的博客程序,今日發布 0.2.0 版。查看項目網站以了解更多細節。


          社區

          Java 焦點博客系列

          由甲骨文傳道士 Roger Brinkley 和 Terrence Barr 主持,Java 焦點博客是一個新的每周秀,包括深度采訪、新聞,以及為并且從 Java 開發人員帶來領悟。最近的客人包括 Mark Reinhold,甲骨文 Java 平臺組的首席架構師。

          練習

          NetBeans 平臺實驗室在 Devoxx,2010-11-12

          請加入 Geertjan Wielenga 和 Anton Epple 在 Devoxx 2010 舉辦的動手實驗室。該實驗室將提供對 NetBeans 平臺的詳盡介紹,它如何工作以及如何開始使用它。

          NetBeans 平臺在甲骨文技術網絡開發人員日,維也納,11-11

          在 11 月 11 日于奧地利維也納舉辦的甲骨文開發人員日上得到對 NetBeans 平臺的介紹。將舉辦探查 NetBeans 平臺背后的概念的研討會,以及學會在 NetBeans 平臺上創建模塊化 Swing 應用程序的動手實驗室。

          發布本期時事通訊的是: D. L. 88250

          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://88250.b3log.org/articles/2010/11/11/1289444119462.html
          posted @ 2010-11-11 10:55 88250 閱讀(156) | 評論 (0)編輯 收藏

          今年2 月份的時候得知 Kenai.com 將與 java.net 合并,半年多了,終于 beta 了。

          Java.net

          目前 kenai.com 依然可以訪問,等上面的項目遷移得差不多就會關閉。


          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://88250.b3log.org/articles/2010/11/11/1289438998967.html
          posted @ 2010-11-11 09:30 88250 閱讀(92) | 評論 (0)編輯 收藏

          昨晚買了個域名:b3log.info。

          現在已經可以使用 www.b3log.info 訪問我的博客了,但還不知道在 GAE 上如何綁定頂級域名。

          本來是想買 b3log.org 的,但對我來說貴了點($14.99),還是先買這個便宜貨($1.99,第一年)將就下。。。。


          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://www.b3log.info/articles/2010/11/09/1289285414380.html
          posted @ 2010-11-09 14:50 88250 閱讀(154) | 評論 (0)編輯 收藏

          B3log Logo按目前的測試情況看,GAE 博客 ——  B3LOG Solo 下周可以進行 0.2.0 版的發布。

          該版本除了修復 Bugs,還增加了文件上傳,改進了緩存,以及加入了新皮膚 i-nove(移植自 iNove)。

           

          這個版本主要是 Bugs 修復及增強穩定性。另外,同步 Google Buzz 的殘次雞肋功能已經移除(只是注釋了界面顯示代碼 - -~)。

          具體改動可以看這里

          B3LOG Solo 0.2.0 發布日期定為 11 月 11 日。


          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://b3log-88250.appspot.com/articles/2010/11/07/1289137888000.html
          posted @ 2010-11-07 21:51 88250 閱讀(131) | 評論 (0)編輯 收藏

          Berkeley DB 是歷史悠久的嵌入式數據庫系統,主要應用在 Unix/Linux 操作系統上,其設計思想是簡單、小巧、可靠、高性能。

          Java 版的 Berkeley DB 4.1.6 在性能上比 4.0 有三倍的性能提升,支持多線程的線程管理和高級內存節點壓縮。

          下載:http://www.oracle.com/technetwork/database/berkeleydb/downloads/index.html

          轉自:http://www.oschina.net/news/12936/berkeley-db-java-4-1-6


          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://b3log-88250.appspot.com/articles/2010/11/07/1289125532482.html
          posted @ 2010-11-07 18:25 88250 閱讀(106) | 評論 (0)編輯 收藏

          今早發現掛在 Google Code 上的 B3log Solo 項目 Downloads 出現了統計問題:

          GoogleCode Bug

          下載數統計于昨天半夜被清 0 了。

          0.0.1-preview1 的上傳日期變 1969 年?在我未出生之前就已經在使用為開發 Google Code?

          。。。。


          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://b3log-88250.appspot.com/articles/2010/11/05/1288919142093.html
          posted @ 2010-11-05 09:06 88250 閱讀(175) | 評論 (0)編輯 收藏

          親愛的QQ用戶:當您看到這封信的時候,我們剛剛作出了一個非常艱難的決定。在360公司停止對QQ進行外掛侵犯和惡意詆毀之 前,我們決定將在裝有360軟件的電腦上停止運行QQ軟件。我們深知這樣會給您造成一定的不便,我們誠懇地向您致歉。同時也把作出這一決定的原因寫在下 面,盼望得到您的理解和支持。

           

          一、保障您的QQ帳戶安全

          近期360強制推廣并脅迫用戶安裝非法外掛“扣扣保鏢”。該軟件劫持了QQ的安全模塊,導致了QQ失去相關功能。在360軟件運行環境下,我們無法保障您的QQ帳戶安全。

          二、對沒有道德底線的行為說不

          360屢屢制造“QQ侵犯用戶隱私”的謠言,對QQ的安全功能進行惡意污蔑。事實上QQ安全模塊絕沒有進行任何用戶隱私數據的掃描、監控,更絕對沒有上傳用戶數據。目前我們已經將QQ安全模塊代碼交由第三方機構檢測,以證明我們的清白。

          更甚的是,360作為一家互聯網安全公司,竟推出外掛軟件,公然站到了“安全”的對立面,對其他公司的軟件進行劫持和控制。這些都是沒有道德底線的行為。

          三、抵制違法行為

          任何商業行為,無論出于何種目的,都應該在國家法律法規的框架下進行。而360竟然采用“外掛”這種非法手段,破壞騰訊公司的正常運營。

          360已經在用戶電腦桌面上對QQ發起了劫持和破壞。我們本可以選擇技術對抗,但考慮再三,我們還是決定不能讓您的電腦桌面成為“戰場”,而把選擇軟件的權利交給您。

          十二年來,QQ有幸能陪伴著您成長;未來日子,我們期待與您繼續同行!

          騰訊公司
          2010年11月3日

           

           

          ----

          關了 360 就可以正常使用 QQ 了,不用卸載嘛。



          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://b3log-88250.appspot.com/articles/2010/11/03/1288791082656.html
          posted @ 2010-11-03 21:31 88250 閱讀(132) | 評論 (0)編輯 收藏

          Hibernate LogoInfoQ Logo

          Hibernate是 最流行的對象關系映射(ORM)引擎之一,它提供了數據持久化和查詢服務。在你的項目中引入Hibernate并讓它跑起來是很容易的。但是,要讓它跑得 好卻是需要很多時間和經驗的。通過我們的使用Hibernate 3.3.1和Oracle 9i的能源項目中的一些例子,本文涵蓋了很多Hibernate調優技術。其中還提供了一些掌握Hibernate調優技術所必需的數據庫知識。

          我們假設讀者對Hibernate有一個基本的了解。如果一個調優方法在Hibernate 參考文檔(下文簡稱HRD)或其他調優文章中有詳細描述,我們僅提供一個對該文檔的引用并從不同角度對其做簡單說明。我們關注于那些行之有效,但又缺乏文檔的調優方法。

          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:

          selectfrom pojoB where pojoa_id in(?,?,?,?,?, ?,?,?,?,?);

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

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

          selectfrom 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參考文檔也沒有提及如何使用新緩存方法。

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

          • 所有流行的Hibernate二級緩存提供商中只有JBoss Cache 2Infinispan 4Ehcache 2支持新方法。OSCacheSwarmCacheCoherenceGigaspaces XAP-Data Grid只支持老方法。
          • 兩種方法共用相同的<cache>配置。例如,它們仍舊使用相同的usage屬性值“transactional|read-write|nonstrict-read-write|read-only”。
          • 多個cache-region適配器仍然內置老方法的支持,理解它能幫助你快速理解新方法。

          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參數來表示),下面是個例子:

          • 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和能源交易。

          查看英文原文:Revving Up Your Hibernate Engine

          轉自:http://www.infoq.com/cn/articles/hibernate_tuning-ii



          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://b3log-88250.appspot.com/articles/2010/11/03/1288789180222.html
          posted @ 2010-11-03 21:00 88250 閱讀(192) | 評論 (0)編輯 收藏

          Hibernate LogoInfoQ Logo

          Hibernate是 最流行的對象關系映射(ORM)引擎之一,它提供了數據持久化和查詢服務。在你的項目中引入Hibernate并讓它跑起來是很容易的。但是,要讓它跑得 好卻是需要很多時間和經驗的。通過我們的使用Hibernate 3.3.1和Oracle 9i的能源項目中的一些例子,本文涵蓋了很多Hibernate調優技術。其中還提供了一些掌握Hibernate調優技術所必需的數據庫知識。

          我們假設讀者對Hibernate有一個基本的了解。如果一個調優方法在Hibernate 參考文檔(下文簡稱HRD)或其他調優文章中有詳細描述,我們僅提供一個對該文檔的引用并從不同角度對其做簡單說明。我們關注于那些行之有效,但又缺乏文檔的調優方法。

          作者 Yongjun Jiao and Stewart Clark 譯者 丁雪豐 發布于 2010年10月26日 上午12時0分

          1.引言

          Hibernate是最流行的對象關系映射(ORM)引擎之一,它提供了數據持久化和查詢服務。在你的項目中引入Hibernate并讓它跑起來是很容易的。但是,要讓它跑得好卻是需要很多時間和經驗的。通過我們的使用Hibernate 3.3.1和Oracle 9i的能源項目中的一些例子,本文涵蓋了很多Hibernate調優技術。其中還提供了一些掌握Hibernate調優技術所必需的數據庫知識。

          我們假設讀者對Hibernate有一個基本的了解。如果一個調優方法在Hibernate 參考文檔(下文簡稱HRD)或其他調優文章中有詳細描述,我們僅提供一個對該文檔的引用并從不同角度對其做簡單說明。我們關注于那些行之有效,但又缺乏文檔的調優方法。

          2.Hibernate性能調優

          調優是一個迭代的、持續進行的過程,涉及軟件開發生命周期(SDLC)的所有階段。在一個典型的使用Hibernate進行持久化的Java EE應用程序中,調優會涉及以下幾個方面:

          • 業務規則調優
          • 設計調優
          • Hibernate調優
          • Java GC調優
          • 應用程序容器調優
          • 底層系統調優,包括數據庫和OS。

          沒有一套精心設計的方案就去進行以上調優是非常耗時的,而且很可能收效甚微。好的調優方法的重要部分是為調優內容劃分優先級。可以用Pareto定律(又稱“80/20法則”)來解釋這一點,即通常80%的應用程序性能改善源自頭20%的性能問題[5]

          相比基于磁盤和網絡的訪問,基于內存和CPU的訪問能提供更低的延遲和更高的吞吐量。這種基于IO的Hibernate調優與底層系統IO部分的調優應該優先于基于CPU和內存的底層系統GC、CPU和內存部分的調優。

          范例1

          我們調優了一個選擇電流的HQL查詢,把它從30秒降到了1秒以內。如果我們在垃圾回收方面下功夫,可能收效甚微——也許只有幾毫秒或者最多幾秒,相比HQL的改進,GC方面的改善可以忽略不計。

          好的調優方法的另一個重要部分是決定何時優化[4]

          積極優化的提倡者主張開始時就進行調優,例如在業務規則和設計階段,在整個SDLC都持續進行優化,因為他們認為后期改變業務規則和重新設計代價太大。

          另一派人提倡在SDLC末期進行調優,因為他們抱怨前期調優經常會讓設計和編碼變得復雜。他們經常引用Donald Knuth的名言“過早優化是萬惡之源 [6]

          為了平衡調優和編碼需要一些權衡。根據筆者的經驗,適當的前期調優能帶來更明智的設計和細致的編碼。很多項目就失敗在應用程序調優上,因為上面提到的“過早優化”階段在被引用時脫離了上下文,而且相應的調優不是被推遲得太晚就是投入資源過少。

          但是,要做很多前期調優也不太可能,因為沒有經過剖析,你并不能確定應用程序的瓶頸究竟在何處,應用程序一般都是這樣演化的。

          對我們的多線程企業級應用程序的剖析也表現出大多數應用程序平均只有20-50%的CPU使用率。剩余的CPU開銷只是在等待數據庫和網絡相關的IO。

          基于上述分析,我們得出這樣一個結論,結合業務規則和設計的Hibernate調優在Pareto定律中20%的那個部分,相應的它們的優先級更高。

          一種比較實際的做法是:

          1. 識別出主要瓶頸,可以預見其中多數是Hibernate、業務規則和設計方面的(其數量視你的調優目標而定;但三到五個是不錯的開端)。
          2. 修改應用程序以便消除這些瓶頸。
          3. 測試應用程序,然后重復步驟1,直到達到你的調優目標為止。

          你能在Jack Shirazi的《Java Performance Tuning》 [7]一書中找到更多關于性能調優階段的常見建議。

          下面的章節中,我們會按照調優的大致順序(列在前面的通常影響最大)去解釋一些特定的調優技術。

          3. 監控和剖析

          沒有對Hibernate應用程序的有效監控和剖析,你無法得知性能瓶頸以及何處需要調優。

          3.1.1 監控SQL生成

          盡管使用Hibernate的主要目的是將你從直接使用SQL的痛苦中解救出來,為了對應用程序進行調優,你必須知道Hibernate生成了哪些 SQL。JoeSplosky在他的《The Law of Leaky Abstractions》一文中詳細描述了這個問題。

          你可以在log4j中將org.hibernate.SQL包的日志級別設為DEBUG,這樣便能看到生成的所有SQL。你還可以將其他包的日志級別設為DEBUG,甚至TRACE來定位一些性能問題。

          3.1.2 查看Hibernate統計

          如果開啟hibernate.generate.statistics,Hibernate會導出實體、集合、會話、二級緩存、查詢和會話工廠的統計信息,這對通過SessionFactory.getStatistics()進行的調優很有幫助。為了簡單起見,Hibernate還可以使用MBean“org.hibernate.jmx.StatisticsService”通過JMX來導出統計信息。你可以在這個網站找到配置范例

          3.1.3 剖析

          一個好的剖析工具不僅有利于Hibernate調優,還能為應用程序的其他部分帶來好處。然而,大多數商業工具(例如JProbe [10])都很昂貴。幸運的是Sun/Oracle的JDK1.6自帶了一個名為“Java VisualVM” [11]的調試接口。雖然比起那些商業競爭對手,它還相當基礎,但它提供了很多調試和調優信息。

          4. 調優技術

          4.1 業務規則與設計調優

          盡管業務規則和設計調優并不屬于Hibernate調優的范疇,但此處的決定對后面Hibernate的調優有很大影響。因此我們特意指出一些與Hibernate調優有關的點。

          在業務需求收集與調優過程中,你需要知道:

          • 數據獲取特性包括引用數據(reference data)、只讀數據、讀分組(read group)、讀取大小、搜索條件以及數據分組和聚合。
          • 數據修改特性包括數據變更、變更組、變更大小、無效修改補償、數據庫(所有變更都在一個數據庫中或在多個數據庫中)、變更頻率和并發性,以及變更響應和吞吐量要求。
          • 數據關系,例如關聯(association)、泛化(generalization)、實現(realization)和依賴(dependency)。

          基于業務需求,你會得到一個最優設計,其中決定了應用程序類型(是OLTP還是數據倉庫,亦或者與其中某一種比較接近)和分層結構(將持久層和服務 層分離還是合并),創建領域對象(通常是POJO),決定數據聚合的地方(在數據庫中進行聚合能利用強大的數據庫功能,節省網絡帶寬;但是除了像 COUNT、SUM、AVG、MIN和MAX這樣的標準聚合,其他的聚合通常不具有移植性。在應用服務器上進行聚合允許你應用更復雜的業務邏輯;但你需要 先在應用程序中載入詳細的數據)。

          范例2

          分析員需要查看一個取自大數據表的電流ISO(Independent System Operator)聚合列表。最開始他們想要顯示大多數字段,盡管數據庫能在1分鐘內做出響應,應用程序也要花30分鐘將1百萬行數據加載到前端UI。經 過重新分析,分析員保留了14個字段。因為去掉了很多可選的高聚合度字段,從剩下的字段中進行聚合分組返回的數據要少很多,而且大多數情況下的數據加載時 間也縮小到了可接受的范圍內。

          范例3

          過24個“非標準”(shaped,表示每小時都可以有自己的電量和價格;如果所有24小時的電量和價格相同,我們稱之為“標準”)小時會修改小時電流交易,其中包括2個屬性:每小時電量和價格。起初我們使用Hibernate的select-before-update特性,就是更新24行數據需要24次選擇。因為我們只需要2個屬性,而且如果不修改電量或價格的話也沒有業務規則禁止無效修改,我們就關閉了select-before-update特性,避免了24次選擇。

          4.2繼承映射調優

          盡管繼承映射是領域對象的一部分,出于它的重要性我們將它單獨出來。HRD [1]中的第9章“繼承映射”已經說得很清楚了,所以我們將關注SQL生成和針對每個策略的調優建議。

          以下是HRD中范例的類圖:


          4.2.1 每個類層次一張表

          只需要一張表,一條多態查詢生成的SQL大概是這樣的:

          select id, payment_type, amount, currency, rtn, credit_card_type from payment

          針對具體子類(例如CashPayment)的查詢生成的SQL是這樣的:

          select id, amount, currency from payment where payment_type=’CASH’ 

          這樣做的優點包括只有一張表、查詢簡單以及容易與其他表進行關聯。第二個查詢中不需要包含其他子類中的屬性。所有這些特性讓該策略的性能調優要比其他策略容易得多。這種方法通常比較適合數據倉庫系統,因為所有數據都在一張表里,不需要做表連接。

          主要的缺點整個類層次中的所有屬性都擠在一張大表里,如果有很多子類特有的屬性,數據庫中就會有太多字段的取值為null,這為當前基于行的數據庫 (使用基于列的DBMS的數據倉庫處理這個會更好些)的SQL調優增加了難度。除非進行分區,否則唯一的數據表會成為熱點,OLTP系統通常在這方面都不 太好。

          4.2.2每個子類一張表

          需要4張表,多態查詢生成的SQL如下:

          select id, payment_type, amount, currency, rtn, credit_card type,
                  case when c.payment_id is not null then 1
               when ck.payment_id is not null then 2
               when cc.payment_id is not null then 3
               when p.id is not null then 0 end as clazz

          from payment p left join cash_payment c on p.id=c.payment_id left join
             cheque_payment ck on p.id=ck.payment_id left join

             credit_payment cc on p.id=cc.payment_id;

          針對具體子類(例如CashPayment)的查詢生成的SQL是這樣的:

          select id, payment_type, amount, currency
          from payment p left join cash_payment c on p.id=c.payment_id;

          優點包括數據表比較緊湊(沒有不需要的可空字段),數據跨三個子類的表進行分區,容易使用超類的表與其他表進行關聯。緊湊的數據表可以針對基于行的 數據庫做存儲塊優化,讓SQL執行得更好。數據分區增加了數據修改的并發性(除了超類,沒有熱點),OLTP系統通常會更好些。

          同樣的,第二個查詢不需要包含其他子類的屬性。

          缺點是在所有策略中它使用的表和表連接最多,SQL語句稍顯復雜(看看Hibernate動態鑒別器的長CASE子句)。相比單張表,數據庫要花更多時間調優數據表連接,數據倉庫在使用該策略時通常不太理想。

          因為不能跨超類和子類的字段來建立復合索引,如果需要按這些列進行查詢,性能會受影響。任何子類數據的修改都涉及兩張表:超類的表和子類的表。

          4.2.3每個具體類一張表

          涉及三張或更多的表,多態查詢生成的SQL是這樣的:

          select p.id, p.amount, p.currency, p.rtn, p. credit_card_type, p.clazz
          from (select id, amount, currency, null as rtn,null as credit_card type,
          1 as clazz from cash_payment union all

          select id, amount, null as currency, rtn,null as credit_card type,
          2 as clazz from cheque_payment union all

          select id, amount, null as currency, null as rtn,credit_card type,
          3 as clazz from credit_payment) p;

          針對具體子類(例如CashPayment)的查詢生成的SQL是這樣的:

          select id, payment_type, amount, currency from cash_payment; 

          優點和上面的“每個子類一張表”策略相似。因為超類通常是抽象的,所以具體的三張表是必須的[開頭處說的3張或更多的表是必須的],任何子類的數據修改只涉及一張表,運行起來更快。

          缺點是SQL(from子句和union all子查詢)太復雜。但是大多數數據庫對此類SQL的調優都很好。

          如果一個類想和Payment超類關聯,數據庫無法使用引用完整性(referential integrity)來實現它;必須使用觸發器來實現它。這對數據庫性能有些影響。

          4.2.4使用隱式多態實現每個具體類一張表

          只需要三張表。對于Payment的多態查詢生成三條獨立的SQL語句,每個對應一個子類。Hibernate引擎通過Java反射找出Payment的所有三個子類。

          具體子類的查詢只生成該子類的SQL。這些SQL語句都很簡單,這里就不再闡述了。

          它的優點和上節類似:緊湊數據表、跨三個具體子類的數據分區以及對子類任意數據的修改都只涉及一張表。

          缺點是用三條獨立的SQL語句代替了一條聯合SQL,這會帶來更多網絡IO。Java反射也需要時間。假設如果你有一大堆領域對象,你從最上層的Object類進行隱式選擇查詢,那該需要多長時間啊!

          根據你的映射策略制定合理的選擇查詢并非易事;這需要你仔細調優業務需求,基于特定的數據場景制定合理的設計決策。

          以下是一些建議:

          • 設計細粒度的類層次和粗粒度的數據表。細粒度的數據表意味著更多數據表連接,相應的查詢也會更復雜。
          • 如非必要,不要使用多態查詢。正如上文所示,對具體類的查詢只選擇需要的數據,沒有不必要的表連接和聯合。
          • “每個類層次一張表”對有高并發、簡單查詢并且沒有共享列的OLTP系統來說是個不錯的選擇。如果你想用數據庫的引用完整性來做關聯,那它也是個合適的選擇。
          • “每個具體類一張表”對有高并發、復雜查詢并且沒有共享列的OLTP系統來說是個不錯的選擇。當然你不得不犧牲超類與其他類之間的關聯。
          • 采用混合策略,例如“每個類層次一張表”中嵌入“每個子類一張表”,這樣可以利用不同策略的優勢。隨著你項目的進化,如果你要反復重新映射,那你可能也會采用該策略。
          • “使用隱式多態實現每個具體類一張表”這種做法并不推薦,因為其配置過于繁縟、使用“any”元素的復雜關聯語法和隱式查詢的潛在危險性。

          范例4

          下面是一個交易描述應用程序的部分領域類圖:

          開始時,項目只有GasDeal和少數用戶,它使用“每個類層次一張表”。

          OilDeal和ElectricityDeal是后期產生更多業務需求后加入的。沒有改變映射策略。但是ElectricityDeal有太多自己的屬性,因此有很多電相關的可空字段加入了Deal表。因為用戶量也在增長,數據修改變得越來越慢。

          重新設計時我們使用了兩張單獨的表,分別針對氣/油和電相關的屬性。新的映射混合了“每個類層次一張表”和“每個子類一張表”。我們還重新設計了查詢,以便允許針對具體交易子類進行選擇,消除不必要的列和表連接。

          4.3 領域對象調優

          基于4.1中對業務規則和設計的調優,你得到了一個用POJO來表示的領域對象的類圖。我們建議:

          4.3.1 POJO調優

          • 從讀寫數據中將類似引用這樣的只讀數據和以讀為主的數據分離出來。
            只讀數據的二級緩存是最有效的,其次是以讀為主的數據的非嚴格讀寫。將只讀POJO標識為不可更改的(immutable)也是一個調優點。如果一個服務層方法只處理只讀數據,可以將它的事務標為只讀,這是優化Hibernate和底層JDBC驅動的一個方法。
          • 細粒度的POJO和粗粒度的數據表。
            基于數據的修改并發量和頻率等內容來分解大的POJO。盡管你可以定義一個粒度非常細的對象模型,但粒度過細的表會導致大量表連接,這對數據倉庫來說是不能接受的。
          • 優先使用非final的類。
            Hibernate只會針對非final的類使用CGLIB代理來實現延時關聯獲取。如果被關聯的類是final的,Hibernate會一次加載所有內容,這對性能會有影響。
          • 使用業務鍵為分離(detached)實例實現equals()和hashCode()方法。
            在多層系統中,經常可以在分離對象上使用樂觀鎖來提升系統并發性,達到更高的性能。
          • 定義一個版本或時間戳屬性。
            樂觀鎖需要這個字段來實現長對話(應用程序事務)[譯注:session譯為會話,conversion譯為對話,以示區別]。
          • 優先使用組合POJO。
            你的前端UI經常需要來自多個不同POJO的數據。你應該向UI傳遞一個組合POJO而不是獨立的POJO以獲得更好的網絡性能。
            有兩種方式在服務層構建組合POJO。一種是在開始時加3.2載所有需要的獨立POJO,隨后抽取需要的屬性放入組合POJO;另一種是使用HQL投影,直接從數據庫中選擇需要的屬性。
            如果其他地方也要查找這些獨立POJO,可以把它們放進二級緩存以便共享,這時第一種方式更好;其他情況下第二種方式更好。

          4.3.2 POJO之間關聯的調優

          • 如果可以用one-to-one、one-to-many或many-to-one的關聯,就不要使用many-to-many。
          • many-to-many關聯需要額外的映射表。
            盡管你的Java代碼只需要處理兩端的POJO,但查詢時,數據庫需要額外地關聯映射表,修改時需要額外的刪除和插入。
          • 單向關聯優先于雙向關聯。
            由于many-to-many的特性,在雙向關聯的一端加載對象會觸發另一端的加載,這會進一步觸發原始端加載更多的數據,等等。
            one-to-many和many-to-one的雙向關聯也是類似的,當你從多端(子實體)定位到一端(父實體)
            這樣的來回加載很耗時,而且可能也不是你所期望的。
          • 不要為了關聯而定義關聯;只在你需要一起加載它們時才這么做,這應該由你的業務規則和設計來決定(見范例5)。
            另外,你要么不定義任何關聯,要么在子POJO中定義一個值類型的屬性來表示父POJO的ID(另一個方向也是類似的)。
          • 集合調優
            如果集合排序邏輯能由底層數據庫實現,就使用“order-by”屬性來代替“sort”,因為通常數據庫在這方面做得比你好。
            集合可以是值類型的(元素或組合元素),也可以是實體引用類型的(one-to-many或many-to-many關聯)。對引用類型集合的調優主要是調優獲取策略。對于值類型集合的調優,HRD [1]中的20.5節“理解集合性能”已經做了很好的闡述。
          • 獲取策略調優。請見4.7節的范例5

          范例5

          我們有一個名為ElectricityDeals的核心POJO用于描述電的交易。從業務角度來看,它有很多many-to-one關聯,例如和 Portfolio、Strategy和Trader等的關聯。因為引用數據十分穩定,它們被緩存在前端,能基于其ID屬性快速定位到它們。

          為了有好的加載性能,ElectricityDeal只映射元數據,即那些引用POJO的值類型ID屬性,因為在需要時,可以在前端通過portfolioKey從緩存中快速查找Portfolio:

          <property name="portfolioKey" column="PORTFOLIO_ID" type="integer"/> 

          這種隱式關聯避免了數據庫表連接和額外的字段選擇,降低了數據傳輸的大小。

          4.4 連接池調優

          由于創建物理數據庫連接非常耗時,你應該始終使用連接池,而且應該始終使用生產級連接池而非Hibernate內置的基本連接池算法。

          通常會為Hibernate提供一個有連接池功能的數據源。Apache DBCP的BasicDataSource[13]是一個流行的開源生產級數據源。大多數數據庫廠商也實現了自己的兼容JDBC 3.0的連接池。舉例來說,你也可以使用Oracle ReaApplication Cluster [15]提供的JDBC連接池[14]以獲得連接的負載均衡和失敗轉移。

          不用多說,你在網上能找到很多關于連接池調優的技術,因此我們只討論那些大多數連接池所共有的通用調優參數:

          • 最小池大小:連接池中可保持的最小連接數。
          • 最大池大小:連接池中可以分配的最大連接數。
            如果應用程序有高并發,而最大池大小又太小,連接池就會經常等待。相反,如果最小池大小太大,又會分配不需要的連接。
          • 最大空閑時間:連接池中的連接被物理關閉前能保持空閑的最大時間。
          • 最大等待時間:連接池等待連接返回的最大時間。該參數可以預防失控事務(runaway transaction)。
          • 驗證查詢:在將連接返回給調用方前用于驗證連接的SQL查詢。這是因為一些數據庫被配置為會殺掉長時間空閑的連接,網絡或數據庫相關的異常也可能會殺死連接。為了減少此類開銷,連接池在空閑時會運行該驗證。

          4.5事務和并發的調優

          短數據庫事務對任何高性能、高可擴展性的應用程序來說都是必不可少的。你使用表示對話請求的會話來處理單個工作單元,以此來處理事務。

          考慮到工作單元的范圍和事務邊界的劃分,有3中模式:

          • 每次操作一個會話。每次數據庫調用需要一個新會話和事務。因為真實的業務事務通常包含多個此類操作和大量小事務,這一般會引起更多數據庫活動(主要是數據庫每次提交需要將變更刷新到磁盤上),影響應用程序性能。這是一種反模式,不該使用它。
          • 使用分離對象,每次請求一個會話。每次客戶端請求有一個新會話和一個事務,使用Hibernate的“當前會話”特性將兩者關聯起來。
            在一個多層系統中,用戶通常會發起長對話(或應用程序事務)。大多數時間我們使用Hibernate的自動版本和分離對象來實現樂觀并發控制和高性能。
          • 帶擴展(或長)會話的每次對話一會話。在一個也許會跨多個事務的長對話中保持會話開啟。盡管這能把你從重新關聯中解脫出來,但會話可能會內存溢出,在高并發系統中可能會有舊數據。

          你還應該注意以下幾點。 

          • 如果不需要JTA就用本地事務,因為JTA需要更多資源,比本地事務更慢。就算你有多個數據源,除非有跨多個數據庫的事務,否則也不需要 JTA。在最后的一個場景下,可以考慮在每個數據源中使用本地事務,使用一種類似“Last Resource Commit Optimization”[16]的技術(見下面的范例6)。
          • 如果不涉及數據變更,將事務標記為只讀的,就像4.3.1提到的那樣。
          • 總是設置默認事務超時。保證在沒有響應返回給用戶時,沒有行為不當的事務會完全占有資源。這對本地事務也同樣有效。
          • 如果Hibernate不是獨占數據庫用戶,樂觀鎖會失效,除非創建數據庫觸發器為其他應用程序對相同數據的變更增加版本字段值。

          范例6

          我們的應用程序有多個在大多數情況下只和數據庫“A”打交道的服務層方法;它們偶爾也會從數據庫“B”中獲取只讀數據。因為數據庫“B”只提供只讀數據,我們對這些方法在這兩個數據庫上仍然使用本地事務。

          服務層上有一個方法設計在兩個數據庫上執行數據變更。以下是偽代碼:

          //Make sure a local transaction on database A exists
          @Transactional (readOnly=false, propagation=Propagation.REQUIRED)
          public void saveIsoBids() {
          //it participates in the above annotated local transaction
          insertBidsInDatabaseA();

          //it runs in its own local transaction on database B
          insertBidRequestsInDatabaseB(); //must be the last operation

          因為insertBidRequestsInDatabaseB()是saveIsoBids ()中的最后一個方法,所以只有下面的場景會造成數據不一致:

          在saveIsoBids()執行返回時,數據庫“A”的本地事務提交失敗。

          但是,就算saveIsoBids()使用JTA,在兩階段提交(2PC)的第二個提交階段失敗的時候,你還是會碰到數據不一致。因此如果你能處理好上述的數據不一致性,而且不想為了一個或少數幾個方法引入JTA的復雜性,你應該使用本地事務。

          (未完待續)

          關于作者

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

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

          [譯注:由于原文較長,中譯版分兩次發布]

          查看英文原文:Revving Up Your Hibernate Engine

          轉自:http://www.infoq.com/cn/articles/hibernate_tuning



          本文是使用 B3log Solo簡約設計の藝術 進行同步發布的
          原文地址:http://b3log-88250.appspot.com/articles/2010/11/03/1288788936673.html
          posted @ 2010-11-03 20:56 88250 閱讀(192) | 評論 (0)編輯 收藏

          僅列出標題
          共9頁: 上一頁 1 2 3 4 5 6 7 8 9 下一頁 
          主站蜘蛛池模板: 增城市| 怀来县| 额济纳旗| 白沙| 凌源市| 简阳市| 诏安县| 望都县| 精河县| 全州县| 洛南县| 镇安县| 江华| 财经| 浦江县| 朝阳县| 承德市| 大厂| 融水| 平果县| 上杭县| 邮箱| 丹寨县| 稻城县| 肥西县| 白河县| 花垣县| 临海市| 巴塘县| 通化县| 阳曲县| 分宜县| 左贡县| 寿光市| 剑川县| 本溪| 金湖县| 当涂县| 聊城市| 福贡县| 会东县|