一、緩存的引入
一個應用主要瓶頸在于資料庫的IO,大家都知道記憶體的速度是遠遠快于硬碟的速度(即使固态硬碟與内容也無法比拟)。應用之中經常會遇到傳回相同的資料(資料字典,行政區劃樹),因為這些資料變化的可能性很小。假如我們使用傳統的方式每次都通過接口與資料庫打交道去請求獲得;是不是每次都既消耗了記憶體資源、網絡資源、資料庫資源、CPU資源,又導緻大量的時間耗費在資料庫查詢,及遠端方法調用上;進而導緻程式性能的惡化。這種場景就是需要使用緩存來解決這類問題。我們把資料緩存在記憶體之中,以後每次擷取直接記憶體之中獲得;使得程式獲得極大的性能提升
二、Springcache
1、SpringCache介紹
SpringCache相當于一個抽象接口,在其底層可以切換各種Cache的實作,當緩存資料時,隻需要注入SpringCache對應的注解,即可實作緩存的功能,大大的提高了緩存的效率,這就是Spring自帶緩存SpringCache的使用原理
Springcache預設的緩存實作是simple(記憶體級),當然可以更換成其他實作,常見的有如下
EhCache:此緩存架構一直伴随着Spring,Hibernate,Mybatis等等。在SpringBoot出來之前都已經廣泛的使用來做為一級緩存
Guava: Google出品的架構,也支援緩存
Redis:在我們日常開發之中經常使用的;并且被大衆廣泛接受的速度極快的緩存
memcached:是一個高性能的分布式記憶體對象緩存系統,用于動态Web應用以減輕資料庫負載。
Spring緩存的接口:
org.springframework.cache.Cache ;
org.springframework.cache.CacheManager
這兩個接口都在context中,一個是用來提供緩存,一個是用來提供管理緩存。
CacheManager是Spring提供的各種緩存技術抽象接口,
Cache接口包含緩存的各種操作(增加、删除、獲得緩存,我們一般不會直接和此接口打交道)。
Spring支援的CacheManager實作如下圖:
使用緩存需要引入的依賴是
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2、注解
2.1、配置注解
EnableCaching
标注于SpringBoot應用啟動類上,添加此注解表示開啟Spring Cache緩存;移除表示關閉緩存。如果在全局配置檔案中添加如下配置,即使在啟動類上标注EnableCaching注解,Spring Cache緩存然後是關閉狀态。
spring:
cache:
type: none
如果應用中自定義獨立于Spring容器的緩存,則不受此配置影響。
CacheConfig
标注于類上,更具體的說是标注于業務服務類上。統一配置如下參數資訊:
參數 | 含義 | 使用說明 |
| 緩存管理器 | 預設指首要的CacheManager |
| 緩存名 | |
| key值生成器 |
在類上統一進行配置,類下的方法自動繼承相應的配置。
2.2、緩存注解
Cacheable
添加緩存的核心注解,分兩種情況:一是對應key值未有緩存資料,先執行方法,然後根據condition和unless條件決定是否添加緩存;二是對應key值已有緩存,不執行方法體,直接傳回資料。
/**
*value:自定義緩存空間名
*key:緩存的key
*/
@Cacheable(value = "cacheSpace",key ="#id" )
public List list(String id){
return Arrays.asList("1","2","3");
}
基礎參數
參數 | 含義 | 使用說明 |
| 緩存管理器 | 預設指首要的CacheManager |
| 緩存名 | |
| key值生成器 | |
| key值 |
參數
keyGenerator
與
key
是互斥的,當
key
存在時
keyGenerator
配置自動失效。
進階參數
參數 | 含義 | 預設值 | 使用說明 |
| 緩存條件 | 訓示滿足條件方執行緩存操作,一般使用參數作為條件 | |
| 否定緩存 | 當條件為 true ,方法的傳回值不會被緩存 | |
| 同步狀态 | false | 表示将方法執行結果以何種方式存入緩存 |
CachePut
更新緩存注解。不管對應key值是否有緩存資料,都執行。
基礎參數
參數 | 含義 | 使用說明 |
| 緩存管理器 | 預設指首要的CacheManager |
| 緩存名 | |
| key值生成器 | |
| key值 |
進階參數
參數 | 含義 | 使用說明 |
| 緩存條件 | 訓示滿足條件方執行緩存操作,一般使用參數作為條件 |
| 否定緩存 | 當條件為 true ,方法的傳回值不會被緩存 |
CacheEvict
主動清除緩存注解。
基礎參數
參數 | 含義 | 使用說明 |
| 緩存管理器 | 預設指首要的CacheManager |
| 緩存名 | |
| key值生成器 | |
| key值 |
進階參數
參數 | 含義 | 預設值 | 使用說明 |
| 緩存條件 | 訓示滿足條件方執行緩存操作,一般使用參數作為條件 | |
| 所有緩存 | false | 表示是否清空目前CacheName對應的所有緩存 |
| 調用前 | false | 表示是否在方法調用前清空緩存 |
3、實作方式
預設cache接口的實作simple(記憶體級),直接使用上面的注解即可,沒有什麼需要配置的,可以自己選擇合适的緩存,
3.1、Ehcache作為緩存
可以在spring-boot-dependencies發現有Echcache依賴,是以我們隻需引入依賴,不需要版本
需要導入的依賴
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
配置
spring:
cache:
type: EHCACHE
還需要一個Ehcache的配置檔案ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:為緩存路徑,ehcache分為記憶體和磁盤兩級,此屬性定義磁盤的緩存位置。參數解釋如下:
user.home – 使用者主目錄
user.dir – 使用者目前工作目錄
java.io.tmpdir – 預設臨時檔案路徑
-->
<diskStore path="java.io.tmpdir/Tmp_EhCache"/>
<!--
defaultCache:預設緩存政策,當ehcache找不到定義的緩存時,則使用這個緩存政策。隻能定義一個。
-->
<!--
name:緩存名稱。
maxElementsInMemory:緩存最大數目
maxElementsOnDisk:硬碟最大緩存個數。
eternal:對象是否永久有效,一但設定了,timeout将不起作用。
overflowToDisk:是否儲存到磁盤,當系統當機時
timeToIdleSeconds:設定對象在失效前的允許閑置時間(機關:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,預設值是0,也就是可閑置時間無窮大。
timeToLiveSeconds:設定對象在失效前允許存活時間(機關:秒)。最大時間介于建立時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,預設是0.,也就是對象存活時間無窮大。
diskPersistent:是否緩存虛拟機重新開機期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:這個參數設定DiskStore(磁盤緩存)的緩存區大小。預設是30MB。每個Cache都應該有自己的一個緩沖區。
diskExpiryThreadIntervalSeconds:磁盤失效線程運作時間間隔,預設是120秒。
memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache将會根據指定的政策去清理記憶體。預設政策是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
clearOnFlush:記憶體數量最大時是否清除。
memoryStoreEvictionPolicy:可選政策有:LRU(最近最少使用,預設政策)、FIFO(先進先出)、LFU(最少通路次數)。
FIFO,first in first out,這個是大家最熟的,先進先出。
LFU, Less Frequently Used,就是上面例子中使用的政策,直白一點就是講一直以來最少被使用的。如上面所講,緩存的元素有一個hit屬性,hit值最小的将會被清出緩存。
LRU,Least Recently Used,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離目前時間最遠的元素将被清出緩存。
-->
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
使用方式還是用上面的注解
3.2、redis作為緩存
引入依賴
<!--springcache依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--使用redis作為緩存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
注意:這裡是把redis作為cache實作進行整合,即springcache整合redis,并不是springboot整合redis
配置
spring:
cache:
type: EHCACHE
redis:
#是否緩存空值
cache-null-values: true
#是否使用字首
use-key-prefix: true
#字首
key-prefix: test
#緩存時間
time-to-live: 10s
redis:
host: localhost
port: 6379
使用方式還是用上面的注解
3.3、memcached作為緩存
下載下傳安裝memcached
位址Windows 下安裝 Memcached | 菜鳥教程
下載下傳後解壓,cmd執行exe檔案
memcached.exe -d install
安裝如果出現failed to install servise or service is already installed,此時使用管理者身份打開cmd執行上面安裝指令即可
然後啟動和停止服務
memcached.exe -d start
memcached.exe -d stop
boot對memcached沒有提供相應的整合,需要使用編碼方式實作用戶端管理,memcached用戶端有一下三個
memcached client for java:最早期用戶端 穩定可靠 使用者群廣(類似redis中的jedis)
SpyMemcached :效率更高
Xmemcached:并發處理更好
這裡示範三個最好的Xmemcached使用
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.7</version>
</dependency>
配置bean
@Configuration
public class XMemcachedConfig {
@Bean
public MemcachedClient memcachedClient() throws IOException {
XMemcachedClientBuilder xMemcachedClientBuilder = new XMemcachedClientBuilder("localhost:11211");
//自己根據需要設定其他參數
xMemcachedClientBuilder.setConnectionPoolSize(20);
MemcachedClient memcachedClient = xMemcachedClientBuilder.build();
return memcachedClient;
}
}
測試
@RequestMapping("mSet")
@ResponseBody
public String mSet() throws InterruptedException, TimeoutException, MemcachedException {
memcachedClient.set("token",0,Arrays.asList("1","2","3"));
return "ok";
}
@RequestMapping("mGet")
@ResponseBody
public String mGet() throws InterruptedException, TimeoutException, MemcachedException {
return memcachedClient.get("token").toString();
}
詳情教程
Memcached 教程 | 菜鳥教程
3.4、jetcache作為緩存
上面幾種緩存技術有以下幾點特點
1、配置方式多樣性,有xml中配置的、有yml配置檔案的、有代碼中寫的,比較散亂
2、緩存有本地方案和遠端方案(redis)
阿裡提供的jetcache設定了本地緩存和遠端緩存的多級緩存解決方案,用來替代spring-cache的
- 本地緩存
LinkedHashMap
Caffeine
- 遠端緩存
Redis(本文選擇遠端方案)
Tair
使用jetcache之後就需要引入spring-boot-starter-cache,加入jetcache依賴
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.2</version>
</dependency>
注意:boot整合jetcache隻需引入上面依賴即可,如果不是boot整合,需要引入的是
jetcache anno和jetcache core兩個依賴
jetcache遠端&本地緩存方案
配置
jetcache:
#area是否進入拼接到緩存的key
areaInCacheName: false
#遠端配置 預設default即可,遠端方案使用redis ,其中poolConfig必須配置
remote:
default:
type: redis
host: localhost
port: 6379
keyConvertor: fastjson
valueEncode: java
valueDecode: java
poolConfig:
maxTotal: 50
minIdle: 5
maxIdle: 20
test:
type: redis
host: localhost
port: 6379
keyConvertor: fastjson
valueEncode: java
valueDecode: java
poolConfig:
maxTotal: 50
minIdle: 5
maxIdle: 20
#本地緩存配置
local:
default:
type: linkedhashmap
keyConvertor: fastjson
valueEncode: java
valueDecode: java
啟動類開啟緩存
@SpringBootApplication
//jetache緩存開啟
@EnableCreateCacheAnnotation
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
/**
*定義緩存對象
* area:和配置檔案配置的要對應,如果配置檔案remote隻有default 這個area可以省略
* name:空間配置 最好使用下劃線或者其他符号和緩存的key隔開
* expire:過期時間 預設機關秒
* timuit:可選配置,預設機關秒
* cacheType:目前緩存類型,隻在本地緩存還是遠端,
* 取值CacheType.BOTH,CacheType.REMOTE CacheType.LOCAL三個取值 預設REMOTE
*/
@CreateCache(area = "test",name = "testCache_",expire = 3600,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.REMOTE)
private Cache<String,String> jetCache;
public void jetCachePut(){
//redis中的key是:test_testCache_token
jetCache.put("token","111111111111111111");
}
public String jetCacheGet(){
return jetCache.get("token");
}
jetcache方法緩存
需要在啟動類開啟方法緩存注解
@SpringBootApplication
//jetache緩存開啟
@EnableCreateCacheAnnotation
//開啟方法緩存
@EnableMethodCache(basePackages = {"com.test"})
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
使用如下
/**
* 方法緩存注解(使用方法緩存時放入緩存的實體類要實作序列化接口)
*/
@Cached(name = "user_",key="#id",expire = 3600,cacheType = CacheType.BOTH)
public User getById(Integer id){
return userDao.getById(id);
}
/**
* 更新資料時同時把緩存中的資料更新
* vaule表示新的資料
*/
@CacheUpdate(name = "user_",key="#user.id",value = "#user")
public void updateUser(User user){
userDao.update(user);
}
/**
* 删除資料時候同時删除緩存中的資料
* vaule表示新的資料
*/
@CacheInvalidate(name = "user_",key = "#id")
public void delUser(Integer id){
userDao.delUser(id);
}
如果同一套系統部署到兩台機器,a機器改了資料但是b沒有收到通知,此時可以使用cache提供的一個定義重新整理緩存的注解
@Cached(name = "user_",key="#id",expire = 3600,cacheType = CacheType.BOTH)
@CacheRefresh(refresh = 5)
public User getById(Integer id){
return userDao.getById(id);
}
檢視jetcache緩存報告報表
隻需增加一個配置
jetcache:
#檢視緩存報告:1min後
statIntervalMinutes: 1