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