亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

Hibernate 性能之緩存

1. 前言

本節課程和大家一起聊聊性能優化方案之:緩存。通過本節課程學習,你將了解到:

  • 什么是緩存,緩存的作用;
  • HIbernate 中的緩存級別;
  • 如何使用緩存。

2. 緩存

2.1 緩存是什么

現實世界里,緩存是一個無處不在的概念。

家里的米桶中都會儲存大米,需要下鍋時,直接從米桶里拿出來,而不是等米下鍋時去商店采購。只有等到米桶中沒有米時,才會去商店。

米桶就是一個類似于緩存的存儲體,它的作用是用來緩存大米。

程序中,通俗講,緩存就是一個用來臨時存儲數據的地方,便于需要時伸手便可拿到。

更專業上講,緩存可以在兩個速度不匹配的設備之間建立一個緩沖帶,適配兩者速度。

2.2 Hibernate 中的為什么需要緩存

要搞清楚 Hibernate 為什么需要緩存,那就要了解 Hibernate 使用緩存做什么?

Hibernate 的任務是幫助開發者發送 SQL 語句,從數據庫中獲取數據。

這個過程并不輕松。從微觀角度上講,Hibernate 要背上行李,通過縱橫交織的網絡交通,到達數據庫服務器,獲取數據。然后背起數據,繼續行走在四通八達的網絡交通,回到程序中。

運氣不好時,碰到網絡擁堵,就會產生延遲,遇到網絡斷線,則會丟失數據。

理論上講,對于每次的數據請求,這個過程都是必須的。

但是,如果多次的請求是同樣數據的時候,也就是用戶的請求 SQL 是一樣的時候,有必要這么不停地來往于數據庫服務器嗎?

面對這種情況,Hibernate 提供的緩存就起作用了,可以緩存曾經從數據庫中獲取過的數據。如果下次再需要時,只需要從緩存中獲取,而無需翻山涉水,通過網絡獲取。

圖片描述

Hibernate 的緩存主要是存儲曾經操作過的數據,程序邏輯向 Hibernate 發送數據請求操作時,Hibernate 會先查詢緩存中有沒有,如果存在,則直接從緩存中獲取,沒有時,才會行走于網絡通道,從數據庫中獲取。

3. Session 緩存

Hibernate 提供有一級和二級緩存,一級緩存也叫 Session 緩存,二級緩存也叫 SessionFactory 緩存。

前面課程中和大家聊過,Session 的使用原則是,需要時創建,用完后關閉,其作用域一般為方法級別。

一級緩存的生命周期和 Session 是一致的,所以,一級緩存中所存儲的數據其生命周期也不長,其實際意義就論情況來看了。

SessionFactory 在前面也討論過,SessionFactory 是應用程序級別的生命周期,所以與其關聯的緩存中所保存的數據也可以長時間存在。

默認情況下,Hibernate 的一級緩存是可以直接使用的,二級緩存是沒有打開的。需要根據實際情況進行選擇。

驗證一級緩存

需求:在 Session 關閉之前,連續查詢相同的學生兩次。

Session session = sessionFactory.openSession();
Transaction transaction = null;
Student stu = null;
try {
	transaction = session.beginTransaction();
	stu = (Student) session.get(Student.class, new Integer(1));
	System.out.println(stu.getStuName());
	// 查詢前面查詢過的學生
	System.out.println("--------------第二次查詢------------------");
	stu = (Student) session.get(Student.class, new Integer(1));
	System.out.println(stu.getStuName());
	transaction.commit();
} catch (Exception e) {
	transaction.rollback();
} finally {
	session.close();
}

運行結果如下:

Hibernate: 
    select
        student0_.stuId as stuId1_3_0_,
        student0_.classRoomId as classRoo6_3_0_,
        student0_.stuName as stuName2_3_0_,
        student0_.stuPassword as stuPassw3_3_0_,
        student0_.stuPic as stuPic4_3_0_,
        student0_.stuSex as stuSex5_3_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=?
Hibernate
--------------第二次查詢------------------
Hibernate

從輸出結果中能得到什么結論?

只有在第一次查詢的時候,Hibernate 才會向數據庫發送 SQL 語句請求,第二查詢時,不需要再發送 SQL 請求,因為緩存中已經存在。

稍微改動一下上述實例,創建兩個 Session 對象,用來查詢同一個學生:

Session session = sessionFactory.openSession();
Transaction transaction = null;
Student stu = null;
try {
	transaction = session.beginTransaction();
	System.out.println("--------------第一次查詢------------------");
	stu = (Student) session.get(Student.class, new Integer(1));
	System.out.println(stu.getStuName());
} catch (Exception e) {
	transaction.rollback();
} finally {
	session.close();
}

session = sessionFactory.openSession();
try {
	transaction = session.beginTransaction();
	// 查詢前面查詢過的學生
	System.out.println("--------------第二次查詢------------------");
	stu = (Student) session.get(Student.class, new Integer(1));
	System.out.println(stu.getStuName());
	transaction.commit();
} catch (Exception e) {
	transaction.rollback();
} finally {
	session.close();
}

查看控制臺上的輸出結果:

Hibernate: 
    select
        student0_.stuId as stuId1_3_0_,
        student0_.classRoomId as classRoo6_3_0_,
        student0_.stuName as stuName2_3_0_,
        student0_.stuPassword as stuPassw3_3_0_,
        student0_.stuPic as stuPic4_3_0_,
        student0_.stuSex as stuSex5_3_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=?
Hibernate
--------------第二次查詢------------------
Hibernate: 
    select
        student0_.stuId as stuId1_3_0_,
        student0_.classRoomId as classRoo6_3_0_,
        student0_.stuName as stuName2_3_0_,
        student0_.stuPassword as stuPassw3_3_0_,
        student0_.stuPic as stuPic4_3_0_,
        student0_.stuSex as stuSex5_3_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=?
Hibernate

每次查詢都會發送 SQL 請求,這是因為 Session 緩存中的數據只能提供給本 Session 對象使用。不能跨 Session 使用。

  • 當調用 save ()、update () 或 saveOrUpdate () 方法傳遞一個對象時,或使用 load ()、 get ()、list ()、iterate () 方法獲得一個對象時,該對象都將被加入到 Session 的內部緩存中;
  • 可以通過調用 close()、clear()、evict() 方法手工清空緩存中的數據。

前面說過的,Session 生命周期很短,與 Session 關聯的一級緩存的生命周期也很短,所以緩存的命中率是很低的。其對系統性能的改善也有限得很。Session 內部緩存的主要作用是保持 Session 內部數據狀態同步。

4. SessionFactory 緩存

SessionFactory 緩存也稱其為二級緩存,是應用程序級別的緩存。二級緩存在默認情況下是沒有啟動的,如果開發者想使用二級緩存所提供的功能,則需要通過一系列的操作流程方能讓其現身。

Hibernate 本身也提供有二級緩存的功能模塊,但只建議用于測試或學習過程。對于生產環境,Hibernae 建議使用專業的第三方緩存框架,如 EhCache 緩存框架。

常用緩存框架:

  • EhCache;
  • OSCache;
  • SwarmCache;
  • JBossCache。

啟動二級緩存

  1. Hibernate 的主配置文件中啟動并指定二級緩存的實現者;
<property name="cache.use_structured_entries">true</property>
<property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  1. 添加 EhCache 相關的 JAR 包。這些 JAR 包都可以在下載的 Hibernate 框架包的 lib 文件夾中找到;
  • ehcache-core-2.4.3.jar;
  • hibernate-ehcache-4.2.0.Final.jar。
  1. 在項目的 src 中添加 EhCache 緩存框架的配置文件 ehcache.xml

這個配置文件可以在下載的 Hibernate 框架包中的 project 目錄下的 etc 中找到。此配置文件中的內容用來配置緩存管理相關信息。

<ehcache>  
<diskStore path="java.io.tmpdir"/>  
<defaultCache  
        maxElementsInMemory="10000"  
        eternal="false"  
        timeToIdleSeconds="120"  
        timeToLiveSeconds="120"  
        overflowToDisk="true"  
        />  
</ehcache>  

配置說明:

  • maxElementsInMemory: 緩存最大數目;
  • eternal : 緩存是否持久;
  • overflowToDisk : 是否保存到磁盤,當系統當機時;
  • timeToIdleSeconds : 當緩存閑置 n 秒后銷毀;
  • timeToLiveSeconds : 當緩存存活 n 秒后銷毀。
  1. 在需要緩存的實體類上添加 @cache 注解
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL,include="all",region="student")  

只有被 @Cache 注解的實體才會被存儲進二級緩存中,此注解有一個 usage 屬性,用來配置緩存的策略,是一個枚舉類型,有如下幾種選擇:

  • CacheConcurrencyStrategy.NONE;
  • CacheConcurrencyStrategy.NONSTRICT_READ_WRITE: 非嚴格讀寫緩存;
  • CacheConcurrencyStrategy.READ_ONLY: 只讀緩存;
  • CacheConcurrencyStrategy.READ_WRITE: 讀寫緩存;
  • CacheConcurrencyStrategy.TRANSACTIONAL: 事務緩存。

Region 指定二級緩存中的區域名,默認為類或者集合的名字。
include 有幾個選項,non-lazy 當屬性延遲抓取打開時,標記為 lazy=“true” 的實體的屬性可能無法被緩存。

做完上面的事情后,再執行前面的兩個 Session 對象查詢同一個學生的代碼,再查看控制臺上的信息:

Hibernate: 
    select
        student0_.stuId as stuId1_1_0_,
        student0_.classRoomId as classRoo5_1_0_,
        student0_.stuName as stuName2_1_0_,
        student0_.stuPassword as stuPassw3_1_0_,
        student0_.stuSex as stuSex4_1_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=?
學生姓名:Hibernate
--------------第二次查詢------------------
學生姓名:Hibernate

第一次查詢時,需要發送 SQL 請求,第二查詢時,不再發送 SQL 請求,因為查詢過的信息已經被存儲在了二級緩存中,Hibernate 會直接從緩存查詢。

二級緩存并不支持緩存 Blob 類型的數據。

5. 小結

本節課和大家一起了解了 Hibernate 提供的緩存機制,Hibernate 提供了一級緩存和二級緩存。一級緩存因生命周期較短,主要用于內部服務。二級緩存因生命周期較長,命中率會較高,緩存中一般存放經常被訪問、改動不頻繁、數量有限的數據。

有了緩存機制的加持,HIbernate 在響應開發者的請求時,又會少了許多延遲。速度對于程序來講,是一個重要的性能指標。