曹 偉,應(yīng) 君,董黎剛
(浙江工商大學(xué)信息與電子工程學(xué)院,浙江杭州310018)
在軟件系統(tǒng)開發(fā)中,數(shù)據(jù)庫操作是影響軟件系統(tǒng)整體性能的一個關(guān)鍵因素,如何獲得更加高效的數(shù)據(jù)讀寫是個一直被人們反復(fù)研究、探索的問題。在Hibernate誕生前,開發(fā)人員使用JDBC來實現(xiàn)與數(shù)據(jù)庫的交互,然而這種解決方案封裝性差,開發(fā)難度大[1]。Hibernate的誕生為面向?qū)ο蟮木幊掏撇ㄖ鸀憽庖恍╅_發(fā)人員已經(jīng)對Hibernate進(jìn)行了詳盡的剖析,其中文獻(xiàn)1提到Hibernate為數(shù)據(jù)庫操作優(yōu)化帶來的巨大改進(jìn)。但開發(fā)中Hibernate的運行效率又被認(rèn)為不如JDBC[2]。其中緩存機(jī)制沒有得到合理應(yīng)用是一個不可忽視的原因。本文將剖析如何利用Hibernate的緩存機(jī)制來提高數(shù)據(jù)讀寫效率,從而提升軟件性能。
緩存是介于應(yīng)用程序和物理數(shù)據(jù)源之間,其作用是為了降低應(yīng)用程序?qū)ξ锢頂?shù)據(jù)源訪問的頻次,從而提高了應(yīng)用的運行性能。緩存內(nèi)的數(shù)據(jù)是對物理數(shù)據(jù)源中的數(shù)據(jù)的復(fù)制,應(yīng)用程序在運行時從緩存讀寫數(shù)據(jù),在特定的時刻或事件會同步緩存和物理數(shù)據(jù)源的數(shù)據(jù)。
Hibernate的緩存結(jié)構(gòu)如圖1所示,可分為一級緩存、二級緩存和查詢緩存[3]。
圖1 Hibernate緩存結(jié)構(gòu)
一級緩存是Session內(nèi)部的緩存,不能被卸載或者刪除,它隨著Session的產(chǎn)生而產(chǎn)生,也隨著Session的滅亡而消失。在一級緩存中,持久化類的每個實例都具有唯一的OID。當(dāng)一個Session對象中對某一個數(shù)據(jù)對象進(jìn)行save()、update()、save Or Update()等操作時都會將該對象存儲在Session的內(nèi)部緩存中。當(dāng)再次請求同一個數(shù)據(jù)對象時會首先在一級緩存中查詢。如此便可有效解決頻繁提交數(shù)據(jù)庫操作的問題。
然而,Hibernate的Session生命周期非常短暫,且Session內(nèi)的緩存不能被其他Session訪問,而實際應(yīng)用中又有這樣的需求,此時可利用Hibernate的二級緩存來解決這個問題。
在Hibernate中,所有的Session都由同一個Session Factory實例來產(chǎn)生[4]。而二級緩存就是Session-Factory級別的緩存,可在不同Session對象間共享,它屬于進(jìn)程范圍或群集范圍的緩存。二級緩存可進(jìn)行配置和更改,并且可以動態(tài)加載和卸載[5]。
當(dāng)Hibernate根據(jù)ID訪問數(shù)據(jù)對象的時候,使用二級緩存的數(shù)據(jù)查詢操作如圖2所示。
圖2 時序圖
當(dāng)Hibernate發(fā)現(xiàn)二級緩存中已經(jīng)有了該條數(shù)據(jù),就會直接拿來用,而不必去數(shù)據(jù)庫查詢,當(dāng)二級緩存中沒有找到時才會到數(shù)據(jù)庫中取。
試想,當(dāng)有N個客戶端請求同一個數(shù)據(jù)對象時如圖3所示,如果系統(tǒng)開啟了二級緩存,那么至少可以減少N-1條數(shù)據(jù)庫通信消息,尤其當(dāng)這個數(shù)據(jù)對象經(jīng)常被訪問的時候,二級緩存的優(yōu)勢將十分明顯。
圖3 多客戶端訪問
但是,二級緩存都基于ID來識別對象的[6],如果希望使用條件查詢那么二級緩存就無用武之地了。為了解決這個問題,Hibernate在設(shè)計中就構(gòu)造了一個查詢緩存也稱之為三級緩存。
所謂查詢緩存(Query Cache)就是針對條件查詢而分配的緩存,它依賴于二級緩存。當(dāng)使用同樣的查詢條件時,Hibernate會從查詢緩存中直接取出結(jié)果。Query Cache用來緩存查詢語句,及查詢結(jié)果集中對象的Identifier與Type.當(dāng)再次使用已緩存的Query時,就可以通過對象的Identifier與Type在二級緩存中查找實際的對象[7]。查詢緩存的應(yīng)用范圍非常有限,因為它對查詢條件非常敏感,查詢命中率低。比如第一條hql取1 20條數(shù)據(jù),第二條hql取1 10條數(shù)據(jù),Hibernate會認(rèn)為這是兩個完全不同的查詢條件,無法利用查詢緩存,會直接向數(shù)據(jù)庫發(fā)起查詢。
一個站點的首頁經(jīng)常被用戶訪問,且每次訪問時前臺都需要去后臺讀取數(shù)據(jù),當(dāng)站點的訪問量較大時后臺會很容易出現(xiàn)卡頓、癱瘓等現(xiàn)象。此時如果采用Hibernate緩存機(jī)制可以有效減少數(shù)據(jù)庫操作,大大提高后臺的承壓能力。以EhCache為例模擬10萬用戶訪問站點的情形,并演示Hibernate緩存機(jī)制是如何優(yōu)化服務(wù)器性能的。
在Hibernate的配置文件中進(jìn)行如下配置:
針對使用二級緩存的類進(jìn)行注解:
該類在二級緩存中并發(fā)策略為Read-write即可讀寫。
這里采用EhCache默認(rèn)的緩存策略即可。
當(dāng)決定將一個數(shù)據(jù)加載到二級緩存中時,需要考慮該數(shù)據(jù)是否經(jīng)常被訪問,是否經(jīng)常被更改,是否會被第三方修改等因素。并非所有的數(shù)據(jù)都適合載入緩存,這里用戶訪問站點首頁的模塊信息不會頻繁更改,數(shù)據(jù)量小,非常適合加載到二級緩存中。
當(dāng)開啟了Hibernate緩存后,模擬10萬用戶去訪問數(shù)據(jù),只有第一個用戶發(fā)出了SQL語句,并且取出數(shù)據(jù)放入緩存中,之后訪問的用戶直接去緩存中取,大大提高了效率??偣灿脮r17 585ms。
當(dāng)關(guān)閉Hibernate緩存后,模擬10萬用戶去訪問數(shù)據(jù)庫,服務(wù)器遲遲沒有響應(yīng),基本上處于癱瘓狀態(tài)??梢奌ibernate緩存機(jī)制在這里起到了非常重要的作用。
緩存技術(shù)在軟件系統(tǒng)中是把雙刃劍,利用得當(dāng)將大幅度提升系統(tǒng)性能,利用不當(dāng)反而會降低系統(tǒng)的響應(yīng)速度。在數(shù)據(jù)量較小的軟件系統(tǒng)中它并不能發(fā)揮太大的作用,但是在數(shù)據(jù)量龐大的系統(tǒng)中,巧妙地配置緩存能夠?qū)崿F(xiàn)非常顯著性能優(yōu)化作用。
[1] Dipti Phutela.Hibernate Vs JDBC[EB/OL].http://www.mindfiresolutions.com/mindfire/Java_Hibernate_JDBC.pdf,2011-06-17.
[2] Jihuanliang.jdbc與 hibernate性能比較總結(jié)[EB/OL].http://blog.csdn.net/jihuanliang/article/details/7965278,2012-09-11.
[3] tutorialspoint.Hibernate Caching[EB/OL].http://www.tutorialspoint.com/hibernate/hibernate_caching.htm,2013-01-20.
[4] 楊帆.設(shè)計模式從入門到精通[M].北京:電子工業(yè)出版社,2010:21-42.
[5] 付京周.精通 Hibernate 3.0——Java數(shù)據(jù)庫持久層開發(fā)實踐[M].北京:人民郵電大學(xué)出版社,2007:307-312.
[6] joyimp.hibernate二級緩存的管理[EB/OL].http://hi.baidu.com/webkiss/item/71e07789ebb16f55850fab0c,2011-05-09.
[7] goncha.關(guān)于 Hibernate Cache[EB/OL].http://www.iteye.com/topic/6593,2004-08-02.