Spring Boot(五)——Spring Boot快取
一、快取簡介
我們知道一個系統的瓶頸通常在與資料庫互動的過程中。記憶體的速度遠遠快於硬碟速度。所以,當我們需要重複地獲取相同的資料的時候,我們一次又一次的請求資料庫或者遠端服務,這無疑是效能上的浪費——會導致大量的時間耗費在資料庫查詢或者遠端方法呼叫上(這些資源簡直太奢侈了),導致程式效能的惡化——於是有了“快取”。快取(Cache)就是資料交換的緩衝區。
隨著時間的積累,應用的使用使用者不斷增加,資料規模也越來越大,往往資料庫查詢操作會成為影響使用者使用體驗的瓶頸,此時使用快取往往是解決這一問題非常好的手段之一。Spring 3開始提供了強大的基於註解的快取支援,可以通過註解配置方式低侵入的給原有Spring應用增加快取功能,提高資料訪問效能。
在Spring Boot中對於快取的支援,提供了一系列的自動化配置,使我們可以非常方便的使用快取。
二、JSR107
Java Caching定義了5個核心介面,分別是CachingProvider, CacheManager, Cache, Entry和 Expiry。
- CachingProvider定義了建立、配置、獲取、管理和控制多個CacheManager。一個應用可以在執行期訪問多個CachingProvider。
- CacheManager定義了建立、配置、獲取、管理和控制多個唯一命名的Cache,這些Cache 存在於CacheManager的上下文中。一個CacheManager僅被一個CachingProvider所擁有。
- Cache是一個類似Map的資料結構並臨時儲存以Key為索引的值。一個Cache僅被一個 CacheManager所擁有。
- Entry是一個儲存在Cache中的key-value對。
- Expiry 每一個儲存在Cache中的條目有一個定義的有效期。一旦超過這個時間,條目為過期 的狀態。一旦過期,條目將不可訪問、更新和刪除。快取有效期可以通過ExpiryPolicy設定。
三、Spring快取抽象
Spring從3.1開始定義了org.springframework.cache.Cache
和org.springframework.cache.CacheManager
- Cache介面為快取的元件規範定義,包含快取的各種操作集合;
- Cache介面下Spring提供了各種xxxCache的實現;如RedisCache,EhCacheCache ,ConcurrentMapCache等;
- 每次呼叫需要快取功能的方法時,Spring會檢查檢查指定引數的指定的目標方法是否 已經被呼叫過;如果有就直接從快取中獲取方法呼叫後的結果,如果沒有就呼叫方法 並快取結果後返回給使用者。下次呼叫直接從快取中獲取。
- 使用Spring快取抽象時我們需要關注以下兩點;
1、確定方法需要被快取以及他們的快取策略
2、從快取中讀取之前快取儲存的資料
四、Spring Boot快取的使用
1、引入spring-boot-starter-cache模組
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2、@EnableCaching開啟快取
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3、使用快取註解
@Cacheable(value = Cache.CONSTANT, key = "'" + CacheKey.ROLES_NAME + "'+#roleIds")
public String getRoleName(String roleIds) {
五、Cache註解詳解
下面結合例子看一下:
@CacheConfig(cacheNames = "users")
public interface UserRepository extends JpaRepository<User, Long> {
@Cacheable
User findByName(String name);
}
@CacheConfig:主要用於配置該類中會用到的一些共用的快取配置。在這裡**@CacheConfig(cacheNames = “users”)**:配置了該資料訪問物件中返回的內容將儲存於名為users的快取物件中,我們也可以不使用該註解,直接通過@Cacheable自己配置快取集的名字來定義。
@Cacheable:配置了findByName函式的返回值將被加入快取。同時在查詢時,會先從快取中獲取,若不存在才再發起對資料庫的訪問。該註解主要有下面幾個引數:
- value、cacheNames:兩個等同的引數(cacheNames為Spring 4新增,作為value的別名),用於指定快取儲存的集合名。由於Spring 4中新增了@CacheConfig,因此在Spring 3中原本必須有的value屬性,也成為非必需項了。
- key:快取物件儲存在Map集合中的key值,非必需,預設按照函式的所有引數組合作為key值,若自己配置需使用SpEL表示式,比如:@Cacheable(key = “#p0”):使用函式第一個引數作為快取的key值,更多關於SpEL表示式的詳細內容可參考官方文件。
- condition:快取物件的條件,非必需,也需使用SpEL表示式,只有滿足表示式條件的內容才會被快取,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有當第一個引數的長度小於3的時候才會被快取,若做此配置上面的AAA使用者就不會被快取,讀者可自行實驗嘗試。
- unless:另外一個快取條件引數,非必需,需使用SpEL表示式。它不同於condition引數的地方在於它的判斷時機,該條件是在函式被呼叫之後才做判斷的,所以它可以通過對result進行判斷。
- keyGenerator:用於指定key生成器,非必需。若需要指定一個自定義的key生成器,我們需要去實現org.springframework.cache.interceptor.KeyGenerator介面,並使用該引數來指定。需要注意的是:該引數與key是互斥的。
- cacheManager:用於指定使用哪個快取管理器,非必需。只有當有多個時才需要使用。
- cacheResolver:用於指定使用那個快取解析器,非必需。需通過org.springframework.cache.interceptor.CacheResolver介面來實現自己的快取解析器,並用該引數指定。
除了這裡用到的兩個註解之外,還有下面幾個核心註解:
@CachePut:配置於函式上,能夠根據引數定義條件來進行快取,它與@Cacheable不同的是,它每次都會真是呼叫函式,所以主要用於資料新增和修改操作上。它的引數與@Cacheable類似,具體功能可參考上面對@Cacheable引數的解析。
@CacheEvict:配置於函式上,通常用在刪除方法上,用來從快取中移除相應資料。除了同@Cacheable一樣的引數之外,它還有下面兩個引數:
- allEntries:非必需,預設為false。當為true時,會移除所有資料。
- beforeInvocation:非必需,預設為false,會在呼叫方法之後移除資料。當為true時,會在呼叫方法之前移除資料。
六、切換為其他快取
下面以常用的EhCache為例,看看如何配置來使用EhCache進行快取管理。
在Spring Boot中開啟EhCache非常簡單,只需要在工程中加入ehcache.xml
配置檔案並在pom.xml
中增加ehcache依賴,框架只要發現該檔案,就會建立EhCache的快取管理器。
在src/main/resources目錄下建立:ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="false" monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir/ehcache"/>
<defaultCache
maxElementsInMemory="50000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<!-- 全域性變數:永不過期-->
<cache name="CONSTANT"
maxElementsInMemory="50000"
eternal="true"
clearOnFlush="false"
overflowToDisk="true"
diskSpoolBufferSizeMB="1024"
maxElementsOnDisk="100000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
</cache>
</ehcache>
<!--
maxElementsInMemory="10000" //Cache中最多允許儲存的資料物件的數量
external="false" //快取中物件是否為永久的,如果是,超時設定將被忽略,物件從不過期
timeToLiveSeconds="3600" //快取的存活時間,從開始建立的時間算起
timeToIdleSeconds="3600" //多長時間不訪問該快取,那麼ehcache 就會清除該快取
這兩個引數很容易誤解,看文件根本沒用,我仔細分析了ehcache的程式碼。結論如下:
1、timeToLiveSeconds的定義是:以建立時間為基準開始計算的超時時長;
2、timeToIdleSeconds的定義是:在建立時間和最近訪問時間中取出離現在最近的時間作為基準計算的超時時長;
3、如果僅設定了timeToLiveSeconds,則該物件的超時時間=建立時間+timeToLiveSeconds,假設為A;
4、如果沒設定timeToLiveSeconds,則該物件的超時時間=min(建立時間,最近訪問時間)+timeToIdleSeconds,假設為B;
5、如果兩者都設定了,則取出A、B最少的值,即min(A,B),表示只要有一個超時成立即算超時。
overflowToDisk="true" //記憶體不足時,是否啟用磁碟快取
diskSpoolBufferSizeMB //設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區
maxElementsOnDisk //硬碟最大快取個數
diskPersistent //是否快取虛擬機器重啟期資料The default value is false
diskExpiryThreadIntervalSeconds //磁碟失效執行緒執行時間間隔,預設是120秒。
memoryStoreEvictionPolicy="LRU" //當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
clearOnFlush //記憶體數量最大時是否清除
maxEntriesLocalHeap="0" //堆記憶體中最大快取物件數,0沒有限制
maxEntriesLocalDisk="1000" //硬碟最大快取個數。
-->
在pom.xml中加入
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.6.1</version>
</dependency>
編寫ehcache配置類
@Configuration
@EnableCaching
public class EhCacheConfig {
/**
* EhCache的配置
*/
@Bean
public EhCacheCacheManager cacheManager(CacheManager cacheManager) {
return new EhCacheCacheManager(cacheManager);
}
/**
* EhCache的配置
*/
@Bean
public EhCacheManagerFactoryBean ehcache() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
return ehCacheManagerFactoryBean;
}
}
完成上面的配置之後,EhCache開啟成功。