Hibernate緩存與集合性能分析

Hibernate做爲一個應用級的數據訪問層封裝,只能在其做用範圍內保持cache中數據的有效性,若是系統與第三方系統共享數據庫的狀況下,Hibernate的Cache機制可能失效。java

Hibernate在本地JVM中維護了一個緩存池,並將從數據庫得到的數據保存到池中以供下次重用。數據庫

外部系統的定義,並不是限於本系統以外的第三方系統,即便在本系統中,若是出現了繞過Hibernate數據存儲機制的其餘數據存取手段,那麼Cache的有效性也必須細加考量。數組

1. Cache分類緩存

Hibernate中的Cache大體分爲兩層:session

(1) Session級別緩存:在Session實現,屬於事務級數據緩衝;一旦事務結束,這個Cache也就失效。此層爲內置實現,無需進行干涉。性能

(2) 二級緩存:Hibernate中對其實力範圍內的數據進行緩存的管理容器。fetch

2. 緩存映射hibernate

配置緩存映射時經過設置類或集合映射的<cache>元素來設定的。如:設計

< cache usage ="transactional | read-write | nonstrict-read-write | read-only" region ="RegionName" include ="all | non-lazy" />

usage:必須。說明了緩存的策略;、代理

  ----read-only:若是隻須要讀取一個持久化類的實例,而無需修改,能夠進行制度緩存,最簡單也是最實用的方法,甚至在集羣中,也能完美地運做;

  ----read-write:若是應用程序須要更新數據,實用讀/寫策略比較合適。但若要求「序列化事務」的隔離級別,則毫不能使用這種緩存策略;

                           若是想在集羣環境中使用此策略,必須保證底層的緩存實現支持鎖定(Locking),Hibernate內置的緩存策略並不支持鎖定。

                           若是在JTA環境中使用緩存,那麼必須指定hibernate.transaction.manager_lookup_class屬性的值。

  ----nonstrict-read-write:非嚴格讀/寫緩存策略。若是隻是偶爾須要更新數據,不多出現兩個事務同時更新同一記錄的狀況,則可用此項。

                           若是在JTA環境中使用緩存,那麼必須指定hibernate.transaction.manager_lookup_class屬性的值。

  ----transactional:Hibernate的事務緩存策略提供了全事務的緩存支持。這樣的緩存只能用於JTA環境中,需指定hibernate.transaction.manager_lookup_class屬性。

region:可選。默認爲類或集合的名字,指定第二級緩存的區域名;

include:可選。默認爲all。當屬性級延遲讀取打開時,標記爲lazy=」true」的實體的屬性可能沒法被緩存。

3. 管理緩存

一級緩存

在應用程序中,當給save()、update()、saveOrUpdate()方法傳遞一個對象或使用load()、get()、list()、iterate()或scroll()方法得到一個對象時,該對象都將被加入到Session的內部緩存中。

當flush()方法被調用時,對象的狀態會和數據庫取得同步。若是不但願此同步操做發生或者正處理大量對象、須要有效管理內存時,就能夠調用evict()方法,從一級緩存中移除這些對象及其集合。

Session還提供了一個contains()方法,用來判斷某個實例是否處於當前session的緩存中。若是要把全部的對象從session的緩存中完全清除,則須要調用Session.clear()方法。

ScrollableResult cats = session.createQuery(「FROM Cat as cat」).scroll(); // 大量數據集 
while (cats.next()){ 
  Cat cat = (Cat)cats.get( 0 ); 
  ... 
  session.evict(cat); // 移除cat緩存 
}

 

二級緩存

對於二級緩存來講,在SessionFactory中定義了許多方法,用於清除緩存中的實例、整個類、集合實例或者整個集合。

如:

sessionFactory.evict(Cat.class, catId); // 清除某個Cat 
sessionFactory.evict(Cat.class); // 清除全部Cats 
sessionFactory.evictCollection("Cat.kittens", catId); // 清除某個kittens對象的集合 
sessionFactory.evictCollection("Cat.kittens"); // 清除全部kittens對象的集合

 

4. 查詢緩存

在Hibernate中查詢的結果集也能夠被緩存。只有當常用一樣的參數進行查詢時,纔會有些用處。

要使用查詢緩存,須要在配置文件中設置:

hibernate.cache.use_query_cache=true

該設置將會建立兩個緩存區域:

① org.hibernate.cache.StandardQueryCache:用於保存查詢結果集;

② org.hibernate.cache.UpdateTimestampsCache:用於保存最近查詢的一系列表的時間戳;

須要注意的是在查詢緩存中,它並不緩存結果集中所包含的實體的確切狀態,只緩存這些實體的標識符屬性的值以及各值類型的結果,因此查詢緩存一般會和二級緩存一塊兒使用。

絕大多數的查詢並不能從查詢緩存中受益,因此Hibernate默認是不進行查詢緩存的。

若是須要進行查詢緩存,須要調用Query.setCacheable(true)方法。這個調用會讓查詢在執行過程當中先從緩存中查找結果,並將本身的結果集放到緩存中去。

若是要對查詢緩存的失效政策進行精確地控制,必須調用Query.setCacheRegion()方法,爲每一個查詢指定其命名的緩存區域。

如:

List cats = session.createQuery(「FROM Cat as cat WHERE cat.kittens = :kit」)
                   .setEntity(「kittens」, kittens)
                   .setMaxResults(20)
                   .setCacheable(true)
                   .setCacheRegion("pages")
                   .list();

 

若查詢須要強行刷新其查詢緩存區域,那麼應該調用Query.setCacheMode(CacheMode.REFRESH)。

這對在其餘進程中修改底層數據或對那些須要選擇性更新特定查詢結果集的狀況特別有用。這是對SessionFactory.evictQueries()更爲有效的替代方案,一樣能夠清除查詢緩存區域。

CacheMode參數用於控制具體的Session如何與二級緩存進行交互,主要屬性以下:

· CacheMode.NORMAL:從二級緩存中讀、寫數據;

· CacheMode.GET:從二級緩存中讀取數據,僅在數據更新時對二級緩存寫數據;

· CacheMode.PUT:僅向二級緩存寫數據,但不從二級緩存中讀取數據;

· CacheMode.REFRESH:僅向二級緩存寫數據,但不從二級緩存中讀數據。經過hibernate.cache.use_minimal_puts的設置,強制二級緩存從數據庫中讀取數據、刷新緩存內容。

5. 提高集合性能

在Hibernate中定義了三種基本類型的集合,分別是值數據集合、一對多關聯和多對多關聯。

這個分類區分了不一樣的表和外鍵關係類型,若是要了解關係模型的全部內容,必須同時考慮」用於Hibernate更新或刪除集合行數據的主鍵結構」,所以獲得如下分類:

· 有序集合類

· 集合(sets)

· 包(bags)

全部的有序集合類(maps/lists/arrays)都擁有一個由<key>和<index>組成的主鍵。

這種狀況下集合類的更新時很是高效的——主鍵已經被有效地索引,所以當Hibernate試圖更新或刪除一行時,能夠迅速找到該行數據。

集合(sets)的主鍵由<key>和其餘元素字段構成。對於有些元素類型來講,效率很低,特別是組合元素或大文本、大二進制字段,數據庫可能沒法有效地對複雜的主鍵進行索引。

另外一方面,對於一對多、多對多關聯,特別是合成的標識符來講,集合也能夠達到一樣的高效性能。

<idbag>映射定義了代理鍵,所以它老是能夠很高效地被更新。事實上,<idbag>擁有着最好的性能表現,Bag的效率是最差的,由於bag容許重複的元素值,也沒有索引字段,所以不可能定義主鍵。

Hibernate沒法判斷重複的行,當這種集合被更改時,Hibernate將會先完整地移除整個集合,而後再從新建立整個集合,所以Bag是很是低效的。

須要注意的是,對於一對多關聯來講,主鍵極可能並非數據庫表的物理主鍵,但在此狀況下,上面的分類仍然有用。

6. Lists、Maps、Sets更新性能分析

對於多對多關聯、值數據集合而言,有序集合類比集合(set)有一個好處,由於Set的內在結構,若是改變了一個元素,Hibernate並不會更新這一行。

對於Set來講,只有在插入和刪除操做時「改變」纔有效。

須要注意的是,數組沒法延遲載入。

大概結論:list、map和idbags市最高效的(非反向)集合類型,set則緊隨其後。

在Hibernate中由於set的語義在關係模型中是最天然的,因此set是最通用的集合類型。

在設計良好的Hibernate領域模型中,一般能夠看到更多的集合,事實上是帶有inverse="true」的一對多的關聯。對於這些關聯,更新操做會在多對一的這一端進行處理,所以對於此類狀況,無需考慮其集合的更新性能。

7. bag和list在反向集合類中的性能分析

對於指明瞭inverse="true」的集合類(如標準的雙向的一對多關聯),能夠在未初始化(fetch)包元素的狀況下直接向bag或list添加新元素。

這是由於Collection.add或者addAll方法對bag或list老是返回true(這點與Set不一樣)。

如:

Parent p = (Parent)session.load(Parent. class , id);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c); // 這裏不會返回整個集合
session.flush();
相關文章
相關標籤/搜索