合理的緩存應用可以極大地提高系統性能,最簡單的是在應用層面做緩存(越高層面做緩存,效果往往越好),直接将資料緩存到伺服器中,以全局map方式存儲。在使用的時候直接從緩存的map中取,而不用連接配接資料庫,進而提升性能。這種方式簡單易行,但是map常駐伺服器記憶體,并且在資料變更(增删改)的時候要手動更新map。
還有一種方式比較通用,就是使用Hibernate二級緩存(SessionFactory級别的全局緩存,程序或叢集級别),是一種通用緩存(一級緩存就不說了,Session級别緩存,hibernate自己管理),hibernate二級緩存多應用在多讀少寫的實體對象中,比如組織機構和系統字典。本文使用hibernate注解方式使用二級緩存,做一個說明(使用Ehcache)。
1、添加ehcache.xml配置檔案
<ehcache>
<!-- maxElementsInMemory="10000" 緩存中最大允許建立的對象數 -->
<!-- eternal="false" 緩存中對象是否為永久的,如果是,逾時設定将被忽略,對象從不過期 -->
<!-- timeToIdleSeconds="120" 緩存資料鈍化時間(設定對象在它過期之前的空閑時間) -->
<!-- timeToLiveSeconds="120" 緩存資料的生存時間(設定對象在它過期之前的生存時間) -->
<!-- overflowToDisk="true" 記憶體不足時,是否啟用磁盤緩存 -->
<diskStore path="c:\\ehcache\"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
</ehcache>
2、hibernate配置檔案中,配置Ehcache相關屬性
<!-- 使用二級緩存:false -->
hibernate.cache.use_second_level_cache=true
<!-- 啟動查詢緩存:false -->
hibernate.cache.use_query_cache=true
<!-- 二級緩存插件:org.hibernate.cache.EhCacheProvider -->
hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
<!-- 是否收集有助于性能調節的統計資料:true -->
hibernate.generate_statistics=true
調試的時候,可以設定log4j的log4j.logger.org.hibernate.cache=debug(記錄二級緩存的活動),實際釋出的時候,注釋掉,以免影響性能。
3、pom檔案中引入相應jar包(Maven項目,如果還在手動添加jar包的,可以嘗試使用maven)
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>3.6.9.Final</version>
</dependency>
這樣就引入了hibernate-ehcache-3.6.9.jar及其依賴包ehcache-core-2.4.3.jar
4、注解方式配置實體
配置了二級緩存後,并不是對所有的實體使用,而是需要指定哪些實體需要用到。如果不配置查詢緩存(查詢緩存會在下面講到),則隻會在根據id查詢的操作中,緩存對象。
在實體上配置@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) 并指定緩存并發政策。ehcache的四種緩存并發政策如下:
read-write (讀寫型) | 提供Read Committed事務隔離級别 在非叢集的環境中适用 适用經常被讀,很少修改的資料 可以防止髒讀 更新緩存的時候會鎖定緩存中的資料 |
nonstrict-read-write (非嚴格讀寫型) | 适用極少被修改,偶爾允許髒讀的資料(兩個事務同時修改資料的情況很少見) 不保證緩存和資料庫中資料的一緻性 為緩存資料設定很短的過期時間,進而盡量避免髒讀 不鎖定緩存中的資料 |
read-only (隻讀型) | 适用從來不會被修改的資料(如參考資料) 在此模式下,如果對資料進行更新操作,會有異常 事務隔離級别低,并發性能高 在叢集環境中也能完美運作 |
@Entity
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "base_dict")
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler", "fieldHandler", "parentDict" })
public class Dict implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5569761987303812150L;
@Id
@Column(name = "id", length = 36)
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
@JsonProperty("id")
private String id;
/** 字典名稱 */
@ForeignShow
@Column(name = "name", length = 200)
private String name;
5、查詢緩存的使用
Query query = session.createQuery(hql);
query.setCacheable(true); //啟用查詢緩存
query.setCacheRegion(“queryCacheRegion”); //設定查詢緩存區域(資料過期政策)
query.list();
Query Cache隻是在特定的條件下才會發揮作用,而且要求相當嚴格:
(1) 完全相同的HQL重複執行。(注意,隻有hql)
(2) 重複執行期間,Query Cache對應的資料表不能有資料變動(比如添、删、改操作)
絕大多數的查詢并不能從查詢緩存中受益,是以Hibernate預設是不進行查詢緩存的。
查詢緩存适用于以下場合:
(1)在應用程式運作時經常使用的查詢語句(參數相同)
(2)很少對與查詢語句檢索到的資料進行插入、删除或更新操作
6、不使用緩存、使用hibernate二級緩存性能對比
在人員資訊清單,性别、政治面貌、職稱、職位使用字典對象存儲,使用緩存後,第一次将相應字典緩存,之後在互動将不會重新查詢資料庫,進而提升系統性能。見下圖實驗結果(機關ms)
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyNzkDNwMjM0EzMykDM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
從圖中可以看到,使用hibernate二級緩存後性能明顯提升一倍。(第一次未使用緩存,是以第一次用時明顯高)
7、應用緩存、hibernate二級緩存性能對比
為了驗證“在應用層面越高的地方做緩存效果越好”這句話,我們來測試下兩種緩存性能之間差别。
測試場景:初始化字典下拉框,下拉框有24個值。結果如下(機關ms)
實驗結果很明顯,應用緩存的效果明顯好于前兩者,但是應用緩存在第一次的時候耗時較長,因為要做初始化操作。在更新資料時,要更新緩存,也會存在一定耗時,是以看到應用緩存的第一個點很高。另外一個時間點也比較特殊,就是hibernate查詢緩存中倒數第二個點,這是因為緩存逾時移除,是以重新從資料庫中查詢(從該值接近不使用查詢緩存可看出)。
要看是否連接配接資料庫查詢,隻需看控制台是否列印出sql語句。
下篇文章将會說下Hibernate一級緩存與懶加載,以上内容不正之處,請指正。