|
1. 持久層的緩存 Hibernate中提供了兩級(jí)Cache,第一級(jí)別的緩存是Session級(jí)別的緩存,它是屬于事務(wù)范圍的緩存。這一級(jí)別的緩存由hibernate自身進(jìn)行管理的,一般情況下無(wú)需進(jìn)行干預(yù),默認(rèn)一級(jí)緩存也是打開(kāi)的;第二級(jí)別的緩存是SessionFactory級(jí)別的緩存,它是屬于進(jìn)程范圍或群集范圍的緩存。這一級(jí)別的緩存比較重量級(jí),可以進(jìn)行配置和更改,并且可以動(dòng)態(tài)加載和卸載。 Hibernate還為查詢(xún)結(jié)果提供了一個(gè)查詢(xún)緩存,它依賴(lài)于第二級(jí)緩存。 每個(gè)事務(wù)都有單獨(dú)的第一級(jí)緩存進(jìn)程范圍或集群范圍,緩存被同一個(gè)進(jìn)程或集群范圍內(nèi)的所有事務(wù)共享并發(fā)訪(fǎng)問(wèn)策略。由于每個(gè)事務(wù)都擁有單獨(dú)的第一級(jí)緩存,因此一級(jí)緩存不會(huì)出現(xiàn)并發(fā)問(wèn)題,無(wú)需提供并發(fā)訪(fǎng)問(wèn)策略,由于多個(gè)事務(wù)會(huì)同時(shí)訪(fǎng)問(wèn)第二級(jí)緩存中相同數(shù)據(jù),因此二級(jí)緩存必須提供適當(dāng)?shù)牟l(fā)訪(fǎng)問(wèn)策略,來(lái)保證特定的事務(wù)隔離級(jí)別數(shù)據(jù)過(guò)期策略沒(méi)有提供數(shù)據(jù)過(guò)期策略。處于一級(jí)緩存中的對(duì)象永遠(yuǎn)不會(huì)過(guò)期,除非應(yīng)用程序顯式清空緩存或者清除特定的對(duì)象必須提供數(shù)據(jù)過(guò)期策略,如基于內(nèi)存的緩存中的對(duì)象的最大數(shù)目,允許對(duì)象處于緩存中的最長(zhǎng)時(shí)間,以及允許對(duì)象處于緩存中的最長(zhǎng)空閑時(shí)間物理存儲(chǔ)介質(zhì)內(nèi)存內(nèi)存和硬盤(pán)。 對(duì)象的散裝數(shù)據(jù)首先存放在基于內(nèi)存的緩存中,當(dāng)內(nèi)存中對(duì)象的數(shù)目達(dá)到數(shù)據(jù)過(guò)期策略中指定上限時(shí),就會(huì)把其余的對(duì)象寫(xiě)入基于硬盤(pán)的緩存中。緩存的軟件實(shí)現(xiàn)是在Hibernate的Session的實(shí)現(xiàn)中包含了緩存的實(shí)現(xiàn)由第三方提供,Hibernate僅提供了緩存適配器(CacheProvider),用于把特定的緩存插件集成到Hibernate中。啟用緩存的方式只要應(yīng)用程序通過(guò)Session接口來(lái)執(zhí)行保存、更新、刪除、加載和查詢(xún)數(shù)據(jù)庫(kù)數(shù)據(jù)的操作,Hibernate就會(huì)啟用第一級(jí)緩存,把數(shù)據(jù)庫(kù)中的數(shù)據(jù)以對(duì)象的形式拷貝到緩存中,對(duì)于批量更新和批量刪除操作,如果不希望啟用第一級(jí)緩存,可以繞過(guò)Hibernate API,直接通過(guò)JDBC API來(lái)執(zhí)行操作。用戶(hù)可以在單個(gè)類(lèi)或類(lèi)集合的粒度上配置第二級(jí)緩存。如果類(lèi)的實(shí)例被經(jīng)常讀但很少被修改,例如常量數(shù)據(jù)集合,就可以考慮使用第二級(jí)緩存。 只有為某個(gè)類(lèi)或集合配置了第二級(jí)緩存,Hibernate在運(yùn)行時(shí)才會(huì)把它的實(shí)例加入到第二級(jí)緩存中。用戶(hù)管理緩存的方式第一級(jí)緩存的物理介質(zhì)為內(nèi)存,內(nèi)存雖然存取速度比較快,奈何由于內(nèi)存容量有限,必須通過(guò)恰當(dāng)?shù)臋z索策略和檢索方式來(lái)限制加載對(duì)象的數(shù)目。Session的 evit()方法可以顯式清空緩存中特定對(duì)象,但這種方法不值得推薦。第二級(jí)緩存的物理介質(zhì)可以是內(nèi)存和硬盤(pán),因此第二級(jí)緩存可以存放大量的數(shù)據(jù),數(shù)據(jù)過(guò)期策略的maxElementsInMemory屬性值可以控制內(nèi)存中的對(duì)象數(shù)目。管理第二級(jí)緩存主要包括兩個(gè)方面:選擇需要使用第二級(jí)緩存的持久類(lèi),設(shè)置合適的并發(fā)訪(fǎng)問(wèn)策略:選擇緩存適配器,設(shè)置合適的數(shù)據(jù)過(guò)期策略。 對(duì)應(yīng)于一級(jí)緩存,當(dāng)應(yīng)用程序調(diào)用Session的save()、update()、savaeOrUpdate()、get()或load(),以及調(diào)用查詢(xún)接口的list()、iterate()或filter()方法時(shí),如果在Session緩存中還不存在相應(yīng)的對(duì)象,Hibernate就會(huì)把該對(duì)象加入到第一級(jí)緩存中。當(dāng)清理緩存時(shí),Hibernate會(huì)根據(jù)緩存中對(duì)象的狀態(tài)變化來(lái)同步更新數(shù)據(jù)庫(kù)。Session為應(yīng)用程序提供了兩個(gè)管理緩存的方法:evict(Object obj):從緩存中清除參數(shù)指定的持久化對(duì)象。 clear():清空緩存中所有持久化對(duì)象。 Hibernate二級(jí)緩存策略的一般過(guò)程如下: 2) 把獲得的所有數(shù)據(jù)對(duì)象根據(jù)ID放入到第二級(jí)緩存中。 3) 當(dāng)Hibernate根據(jù)ID訪(fǎng)問(wèn)數(shù)據(jù)對(duì)象的時(shí)候,首先從Session一級(jí)緩存中查;查不到,如果配置了二級(jí)緩存,那么從二級(jí)緩存中查;查不到,再查詢(xún)數(shù)據(jù)庫(kù),把結(jié)果按照ID放入到緩存。 4) 刪除、更新、增加數(shù)據(jù)的時(shí)候,同時(shí)更新緩存。 Hibernate二級(jí)緩存策略,是針對(duì)于ID(主鍵)查詢(xún)的緩存策略,對(duì)于條件查詢(xún)則毫無(wú)作用。為此,Hibernate提供了針對(duì)條件查詢(xún)的Query Cache,就是之前說(shuō)的查詢(xún)緩存。更新緩存的代價(jià)比從緩存中查詢(xún)的代價(jià)大得多,因?yàn)榫彺娴讓邮莻€(gè)大Map或者是個(gè)大Item(org.hibernate.cache.ReadWriteCache.Item)進(jìn)行存儲(chǔ)的。而且存儲(chǔ)的一般都是對(duì)象,空間移動(dòng),存取,修改的代價(jià)可想而知。那么什么樣的數(shù)據(jù)適合存放到第二級(jí)緩存中? 1) 很少被修改的數(shù)據(jù) 2) 不是很重要的數(shù)據(jù),允許出現(xiàn)偶爾并發(fā)的數(shù)據(jù) 3) 不會(huì)被高并發(fā)訪(fǎng)問(wèn)的數(shù)據(jù) 4) 參考數(shù)據(jù),指的是供應(yīng)用參考的常量數(shù)據(jù),它的實(shí)例數(shù)目有限,它的實(shí)例會(huì)被許多其他類(lèi)的實(shí)例引用,實(shí)例極少或者從來(lái)不會(huì)被修改。 那么什么樣的數(shù)據(jù)不適合存放到第二級(jí)緩存中? 1) 經(jīng)常被修改的數(shù)據(jù),代價(jià)太大了,得不償失。 2) 金錢(qián)敏感數(shù)據(jù),絕對(duì)不允許出現(xiàn)并發(fā) 3) 與其他應(yīng)用共享的數(shù)據(jù)。 Hibernate的二級(jí)緩存往往要借助第三方的工具,下面是幾種常用的緩存工具: 1):EhCache:可作為進(jìn)程范圍的緩存,存放數(shù)據(jù)的物理介質(zhì)可以是內(nèi)存或硬盤(pán),對(duì)Hibernate的查詢(xún)緩存提供了支持。 2):OSCache:可作為進(jìn)程范圍的緩存,存放數(shù)據(jù)的物理介質(zhì)可以是內(nèi)存或硬盤(pán),提供了豐富的緩存數(shù)據(jù)過(guò)期策略,對(duì)Hibernate的查詢(xún)緩存提供了支持。 3):SwarmCache:可作為群集范圍內(nèi)的緩存,但不支持Hibernate的查詢(xún)緩存。 4):JBossCache:可作為群集范圍內(nèi)的緩存,支持事務(wù)型并發(fā)訪(fǎng)問(wèn)策略,對(duì)Hibernate的查詢(xún)緩存提供了支持。 5):當(dāng)然了還有我們這次要介紹的Memcacahe Memcacahe這個(gè)開(kāi)源項(xiàng)目,是一個(gè)高性能的分布式的內(nèi)存對(duì)象緩存系統(tǒng),通過(guò)在內(nèi)存里維護(hù)一個(gè)統(tǒng)一的巨大的Hash表,能夠用來(lái)存儲(chǔ)各種格式的數(shù)據(jù)。其實(shí)Memcache最開(kāi)始是作為高可用集群的緩存解決方案的。一些互聯(lián)網(wǎng)的非敏感信息就放到Memcache中,用戶(hù)第一次訪(fǎng)問(wèn)還是從數(shù)據(jù)庫(kù)里面進(jìn)行查詢(xún),之后放到緩存中,之后用戶(hù)的訪(fǎng)問(wèn)都從緩存中去取。至于緩存更新,根據(jù)業(yè)務(wù)不同,有不同的方案,比如一旦數(shù)據(jù)發(fā)生變更,就是先更新數(shù)據(jù)庫(kù)后,立即更新緩存數(shù)據(jù),還有一種做法就是對(duì)于十分不敏感的數(shù)據(jù)僅僅更新緩存,定時(shí)向數(shù)據(jù)庫(kù)進(jìn)行同步和更新。它還可以作為高分布式系統(tǒng)的Session的解決方案。Memcache還可以作為Hibernate的二級(jí)緩存插件。hibernate-memcached這個(gè)java類(lèi)庫(kù)用于在Hibernate中使用Memcached作為一個(gè)二級(jí)分布式緩存。支持實(shí)體和查詢(xún)緩存。 2. 搭建環(huán)境 hibernate-memcached是Google的托管項(xiàng)目,開(kāi)發(fā)源代碼。 在http://code.google.com/p/hibernate-memcached/可以下載
Memcache自身需要一個(gè)服務(wù)端,windows版本,筆者找了好久,終于在http://code./memcached/找到,讀者可自己下載。下載后是一個(gè)exe文件。 要想在我們自己的項(xiàng)目中使用好Memcache,還得使用一個(gè)Memcacahe客戶(hù)端程序。筆者極力推崇dennis兄的Xmemcached作為客戶(hù)端。可以從 http://code.google.com/p/xmemcached/下載。當(dāng)然memcache還需要依賴(lài)slf4j-api包。 之后就是JPA規(guī)范與Hibernate相關(guān)包的導(dǎo)入了,當(dāng)然可以借助IDE了,筆者前面也有相關(guān)筆記寫(xiě)到,在此就不再贅述了。搭建好環(huán)境后的工作空間如下 首先先配置JPA的屬性
當(dāng)著明人不說(shuō)暗話(huà),其實(shí)這段配置中,其他配置都好說(shuō),就是hibernate.cache.use_structured_entries這個(gè)配置,筆者查了半天,不是很明白。文檔說(shuō)是結(jié)構(gòu)化存儲(chǔ),沒(méi)說(shuō)怎么個(gè)結(jié)構(gòu)化存儲(chǔ)~網(wǎng)上都說(shuō)是人性化存儲(chǔ),筆者的疑問(wèn)是,什么是人性化存儲(chǔ)?怎么樣進(jìn)行人性化存儲(chǔ)?不是特別明白,這個(gè)大家可以討論。 下面咱們來(lái)看實(shí)體POJO
其中類(lèi)上的標(biāo)注,詳細(xì)解釋請(qǐng)看http://www./problems/49111。它是二級(jí)緩存的讀取策略。針對(duì)不同的實(shí)體需求,配置不同的訪(fǎng)問(wèn)策略。請(qǐng)注意注解的全路徑。 DAO代碼就不全給出了,比較簡(jiǎn)單,僅僅給出片段即可
4. 測(cè)試
我們先將Hibernate的二級(jí)緩存在配置文件中關(guān)掉。就是設(shè)為false運(yùn)行測(cè)試代碼試試。 運(yùn)行后控制臺(tái)效果如下
從輸出的sql語(yǔ)句可以看到執(zhí)行后,先從數(shù)據(jù)庫(kù)查詢(xún),之后去取記錄從Hibernate的一級(jí)緩存中獲得,就不在查庫(kù)了。好的,我們?cè)俅芜\(yùn)行測(cè)試代碼,輸出的效果和第一次運(yùn)行一樣,還是要先查一道數(shù)據(jù)庫(kù),控制臺(tái)會(huì)輸出sql語(yǔ)句。 下面我們開(kāi)啟memcached服務(wù),將Hibernate的二級(jí)緩存配置打開(kāi)。運(yùn)行測(cè)試代碼,第一次運(yùn)行和沒(méi)開(kāi)啟二級(jí)緩存前效果一樣,但是第二次運(yùn)行測(cè)試代碼,效果如下
此時(shí)并沒(méi)有再次訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),而是直接從memcached緩存中獲取。之后數(shù)次運(yùn)行測(cè)試代碼,效果都是從緩存獲取,那么我們關(guān)閉memcached服務(wù)。再運(yùn)行測(cè)試代碼,效果如下
發(fā)現(xiàn)依然和沒(méi)開(kāi)啟緩存的效果是一樣的,從數(shù)據(jù)庫(kù)去取。證明緩存生效。 而且我們還可以通過(guò)XMemcached客戶(hù)端代碼獲取緩存對(duì)象,代碼如下
5. 總結(jié) 當(dāng)然,就像剛開(kāi)始說(shuō)的,Hibernate還有很多其他緩存組件可用,不過(guò)筆者認(rèn)為memcache最大的優(yōu)點(diǎn)就是達(dá)到了分布式的二級(jí)緩存。尤其是面向集群系統(tǒng),并發(fā)量又比較大,多個(gè)服務(wù)器之間公用一個(gè)緩存,減輕各個(gè)Node的負(fù)載,當(dāng)然放到二級(jí)緩存的數(shù)據(jù)并發(fā)量不能太大。讀取操作倒是可以,如果遇到更新操作會(huì)有極小的數(shù)據(jù)有效性的延遲。就是數(shù)據(jù)更新了,緩存還沒(méi)來(lái)得及更新呢,就被另一個(gè)線(xiàn)程取走了。這個(gè)時(shí)候就是無(wú)效的,當(dāng)然客戶(hù)端XMemcached的CAS進(jìn)行樂(lè)觀鎖鎖定,顯示報(bào)出異常等等措施,但是對(duì)于系統(tǒng)的運(yùn)行效率上就得犧牲一些。總得來(lái)說(shuō)現(xiàn)在使用memcache作為ORM二級(jí)緩存漸漸成為了趨勢(shì)。
PS:參考資料 http://developer.51cto.com/art/200909/153715.htm |
|
|