Cache就是緩衝,它往往是提高系統效能的最重要手段,對資料起到一個蓄水池和緩衝的作用。Cache對於大量依賴資料讀取操作的系統而言尤其重要。在大並發量的情況下,如果每次程式都需要向資料庫直接做查詢操作,它們所帶來的效能開銷顯而易見,頻繁的網路傳輸、資料庫磁碟的讀寫操作都會大大降低系統的整體效能。此時,如果能把資料在本地記憶體中保留一個鏡像,下次訪問時只需從記憶體中直接擷取,那麼顯然可以帶來顯著的效能提升。引入Cache機制的痛點是如何保證記憶體中資料的有效性,否則髒資料的出現將會給系統帶來難以預知的嚴重後果。雖然一個設計得很好的應用程式不用Cache也可以表現出讓人接受的效能,但毫無疑問,一些對讀操作要求很高的應用程式可以通過Cache取得更高的效能。對於應用程式,Cache通過記憶體或磁碟儲存了資料庫中當前有關資料狀態,它是一個存於當地的資料備份。Cache位於資料庫和應用程式之間,從資料庫中更新資料,並給程式提供資料。
Hibernate實現了良好的Cache機制,可以藉助Hibernate內部的Cache迅速提高系統資料讀取效能。Hibernate中的Cache可分為兩層:一級Cache和二級Cache。
一級Cache
Session實現了第一級Cache,它屬於事務級資料緩衝。一旦事務結束,這個Cache也隨之失效。一個Session的生命週期對應一個資料庫事務或一個程式事務。
Session-cache保證在一個Session中兩次請求同一個對象時,取得的對象是同一個Java執行個體,有時它可以避免不必要的資料衝突。另外,它還能保證另一些重要的效能。
在對一個對象循環參考時,不至於產生堆疊溢位。
當資料庫事務結束時,對於同一資料庫行,不會產生資料衝突,因為對於資料庫中的一行,至多有一個對象來表示它。
一個事務中可能會有很多個處理單元,在一個處理單元中做的操作都會立即被另外的處理單元得知。
不用刻意去開啟Session-cache,它總是被開啟並且不能被關閉。當使用save()、update()或saveOrUpdate()來儲存資料更改,或通過load()、find()、list()等方法來得到對象時,對象就會被加入到Session-cache。
如果要同步很大數量的對象,這是需要有效地管理Cache,可以用Session的evict()方法從一級Cache中移除對象。例如:
Session session = HibernateUtil.currentSession();Transaction tx = session.beginTransaction() ;for(int i=0; i<100000 ;i++) {Student stu = new Student();….session.save(stu);}tx.commit();session.close();
在儲存50 000個對象時,程式可能拋出OutOfMemoryException異常,因為Hibernate在一級Cache緩衝了新加入資料庫的所有對象。要解決這個問題,首先設定JDBC批處理數量到一個合理的數值(一般是10~20)。在hibernate.properties設定檔中設定如下:
hibernate.jdbc.batch_size 20
或在hibernate.cfg.xml中設定如下:
<property name=”hibernate.jdbc.batch_size”> 20 </property>
然後在一定的時候提交更改並清空Session的Cache:
Session session = HibernateUtil.currentSession();Transaction tx = session.beginTransaction() ;for(int i=0; i<100000 ;i++) {Student stu = new Student();….session.save(stu);if(i%20==0) { //每儲存完20個Student對象後,進行以下操作session.flush(); //提交更新session.clear(); //清除Cache,釋放記憶體}}tx.commit();session.close();
二級Cache
二級Cache是SessionFactory範圍內的緩衝,所有的Session共用同一個二級Cache。在二級Cache中儲存持久性執行個體的散裝形式的資料。二級Cache的內部是如何?的並不重要,重要的是採用哪種正確的緩衝策略,以及採用哪種Cache Providers。持久化不同的資料需要不同的Cache策略,比如說一些因素將影響到Cache策略選擇:資料的讀/寫比例、資料表是否能被其他的應用程式所訪問等。對於一些讀/寫比例高的資料可以開啟它的緩衝,允許這些資料進入二級緩衝容器有利於系統效能的最佳化;而對於能被其他應用程式訪問的資料對象,最好將此對象的二級Cache選項關閉。
設定Hibernate的二級Cache需要分兩步進行:首先確認使用什麼資料並發策略,然後配置緩衝到期時間和設定Cache提供器。
有4種內建的Hibernate資料並發衝突策略,代表了資料庫隔離等級,如下所示。
transactional:僅在受管理的環境中可用。它保證可重讀的實物隔離等級,可以對讀/寫比例高、很少更新的資料採用這種策略。
read-write:使用timestamp機制維護讀已提交交易隔離等級。可以對讀/寫比例高、很少更新的資料採用這策略。
nonstrict-read-write:不保證Cache和資料庫之間的資料一致性。使用此策略時,應該設定足夠短的緩衝到期時間,否則可能從緩衝中讀出髒資料。當一些資料極少改變,並且當這些資料和資料庫有一部分不一致但影響不大時,可以使用此策略。
read-only:當確保資料永不改變時,可以使用此策略。
確定了Cache策略之後,就要挑選一個合適高效的Cache提供器,它作為外掛程式被Hibernate調用。Hibernate允許使用下述幾種快取區外掛程式。
EhCache:可以在JVM中作為一個簡單進程範圍的緩衝,它可以把緩衝的資料放入記憶體或磁碟,並支援Hibernate中可選用的查詢快取。
OpenSymphony OSCache:和EhCache相似,並且它提供了豐富的緩衝到期策略。
SwarmCache:可作為叢集範圍的緩衝,但不支援查詢快取。
JBossCache:可作為叢集範圍的緩衝,但不支援查詢快取。
上述4種快取區外掛程式的對比情況列於表9-3中。
表9-3 4種快取區外掛程式的對比情況
緩 存 插 件
支 持 只 讀
支援非嚴格讀寫
支 持 讀 寫
支 持 事 務
EhCache
是
是
是
OSCache
是
是
是
SwarmCache
是
是
JBossCache
是
是
它們的提供器列於表9-4中。
表9-4 緩衝策略的提供器
緩 存 插 件
提供器(Cache Providers)
Hashtable(只能測試時使用)
org.hibernate.cache.HashtableCacheProvider
EhCache
org.hibernate.cache.EhCacheProvider
OSCache
org.hibernate.cache.OSCacheProvider
SwarmCache
org.hibernate.cache.SwarmCacheProvider
JBossCache
org.hibernate.cache.TreeCacheProvider
在預設情況下,Hibernate使用EhCache進行JVM層級的緩衝。使用者可以通過設定Hibernate設定檔中的hibernate.cache.provider_class的屬性,指定其他的緩衝策略,該緩衝策略必須實現org.hibernate.cache.CacheProvider介面。
在Hibernate中使用EhCache
EhCache是一個純Java程式,可以在Hibernate中作為一個外掛程式引入。它具有運行速度快、結構簡單、佔用記憶體小、很小的依賴性、支援多CPU伺服器、文檔齊全等特點。
在Hibernate中使用EhCache,需要在hibernate.cfg.xml中設定如下:
<property name=” hibernate.cache.provider_class”>org.hibernate.cache.EhCacheProvider</property>
EhCacheProvider類位於hibernate3.jar包中,而不是位於ehcache-1.1.jar包中。EhCache有自己的配置文檔,名為ehcache.xml。在Hibernate3.x中的etc目錄下有ehcache.xml的示範檔案,將其複製應用程式的src目錄下(編譯時間會把ehcache.xml複製到WEB-INF/classess目錄下),對其中的相關值變更以和自己的程式相適合。進行配置後,在ehcache.xml檔案中的全部代碼如下:
<diskStore path="d:\\cache"/> //設定cache.data檔案的存放位置 <defaultCachemaxElementsInMemory="10000" //緩衝中允許建立的最大對象數eternal="false" //緩衝中對象是否為永久的timeToIdleSeconds="120" //快取資料鈍化時間(即對象在它到期前的空閑時間)timeToLiveSeconds="120" //快取資料存留時間(即對象在它到期前的存留時間)overflowToDisk="true" //是否啟用磁碟緩衝/> <cache name="Student" //使用者自訂的Cache配置maxElementsInMemory="10000"eternal="false"timeToIdleSeconds="300"timeToLiveSeconds="600"overflowToDisk="true"/></ehcache>
此外,還需要在持久化類的對應檔中進行配置。例如,Group(班級)和Student(學生)是一對多的關係,它們對應的資料表分別是t_group和t_student。現在要把Student類的資料進行二級緩衝,這需要在兩個對應檔(Student.hbm.xml和Group.hbm.xml)中都對二級緩衝進行配置。
在Group.hbm.xml中配置二級緩衝如下:
……<hibernate-mapping><class name="Group" table="t_group" lazy="false">……<set name="students" cascade="save-update" inverse="true" <!--關係由Student維護-->lazy="true"><cache usage="read-write"/> //<!--集合中的資料將被緩衝--><key column="id"/><one-to-many class="Student"/></set></class></hibernate-mapping>……
上述檔案雖然在<set>標記中設定了<cache usage="read-write"/>,但Hibernate僅把和Group相關的Student的主鍵id加入到緩衝中,如果希望把整個Student的散裝屬性都加入到二級緩衝中,還需要在Student.hbm.xml檔案的<class>標記中加入<cache>子標記,如下所示:
<class name="Student" table="t_student" ><cache usage="read-write" /> <!--cache標記需跟在class標記後-->……</class>