0. 引言
隨著redis的普及,更多的同學對redis分布式緩存更加熟悉,但在一些實際場景中,其實并不需要用到redis,使用更加簡單的本地緩存即可實現(xiàn)我們的緩存需求。
今天,我們一起來看看本地緩存組件ehcache
1. ehcache簡介
1.1 簡介
ehcache是基于java開發(fā)的本地緩存組件,無需單獨安裝部署,只要引入jar包就可利用它來實現(xiàn)緩存。
所謂本地緩存,就是指存儲在JVM堆內(nèi)存中的臨時緩存數(shù)據(jù),當然ehcache本身也支持Off-Heap Store機制
來使用堆外內(nèi)存,本地緩存相較于redis性能和響應速度更高。
Ehcache的本地緩存還支持過期時間、最大容量、持久化等特性,使得它可以適用于各種不同的緩存場景。
官方文檔地址:https://www.ehcache.org/documentation/
1.2 本地緩存與redis的區(qū)別
本地緩存與redis的區(qū)別在于:
-
架構:
本地緩存基于單機架構,即數(shù)據(jù)僅本機可用,無法共享給其他服務。除非使用服務調(diào)用來獲取。而redis本身基于分布式架構,支持跨服務調(diào)取。
所以當數(shù)據(jù)需要分布式調(diào)用時,則適用于redis,如果數(shù)據(jù)只需要本地獲取,則可考慮本地緩存 -
性能:
本地緩存本身基于本機內(nèi)存,沒有網(wǎng)絡IO消耗,所以性能上大大高于redis,但是如果數(shù)據(jù)量較大,則還是要考慮使用redis,本地緩存僅適用于數(shù)據(jù)量小、結構簡單的數(shù)據(jù)場景,不適合復雜的業(yè)務數(shù)據(jù)
-
功能拓展:
redis支持持久化、訂閱模式、集群、主從模式等,而ehcache更傾向于簡單的緩存功能場景,雖然也支持持久化,但是本身并不建議用它來做大型或復雜場景的緩存。如果場景比較簡單輕量,對延遲有較高要求,則可選擇本地緩存
2. ehcache使用
1、創(chuàng)建一個springboot項目,這里我的springboot版本為2.6.13
2、引入ehcahe組件依賴
這里需要注意的是net.sf.ehcache
是ehcache2.X 與 org.ehcache
是echcache3.X,兩個版本配置有區(qū)別
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.6.13</version>
</dependency>
3、在啟動類上添加@EnableCaching
注解,開啟緩存
@SpringBootApplication
@EnableCaching
public class LocalCacheDemoApplication {
public static void main(String[] args) {
SpringApplication.run(LocalCacheDemoApplication.class, args);
}
}
4、在配置文件application.yml
中添加配置
spring:
profiles:
active: dev
cache:
type: ehcache
ehcache:
config: classpath:ehcache.xml
5、在resources文件夾下創(chuàng)建配置文件ehcache.xml
,注意這里單獨創(chuàng)建了一個name為user的緩存,用于后續(xù)保存用戶信息緩存。如果有不同的緩存需要使用不同的name的,需要單獨創(chuàng)建cache標簽
標簽介紹:
defaultCache: 默認緩存配置標簽
cache 指定緩存標簽,name表示緩存名稱
diskStore 數(shù)據(jù)存儲磁盤路徑
屬性介紹:
eternal: 緩存是否永久有效,如果為 true 則忽略timeToIdleSeconds 和 timeToLiveSeconds
maxElementsInMemory:最多緩存多少個key
overflowToDisk: 緩存超限時是否寫入磁盤,默認為true
overflowToOffHeap: 堆內(nèi)存超限時是否使用堆外內(nèi)存,企業(yè)版功能,收費
diskPersistent:緩存是否持久化
timeToLiveSeconds:緩存多久過期
timeToIdleSeconds:緩存多久沒有被訪問就過期
diskExpiryThreadIntervalSeconds:磁盤緩存過期檢查線程運行時間間隔
memoryStoreEvictionPolicy:緩存淘汰策略, LFU:最近最少使用的元素先移出; FIFO:最先進入的元素被移出; LRU:使用越少的元素被移出
maxBytesLocalHeap:緩存最大占用JVM堆內(nèi)存,0表示不限制,單位支持K、M或G
maxBytesLocalOffHeap: 緩存最大占用堆外內(nèi)存,0表示不限制,單位支持K、M或G,企業(yè)版功能,收費
maxBytesLocalDisk:緩存最大占用磁盤,0表示不限制,單位支持K、M或G
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToLiveSeconds="3600"
timeToIdleSeconds="0"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="user"
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToLiveSeconds="3600"
timeToIdleSeconds="0"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
<!-- 存儲到磁盤時的路徑-->
<diskStore path="/Users/wuhanxue/Downloads/ehcache" />
</ehcache>
6、緩存使用,在獲取方法中使用@Cacheable
注解,在更新方法中使用@CachePut
注解。
我這里模擬就沒有訪問數(shù)據(jù)庫查詢數(shù)據(jù)了,大家在實際書寫的時候可以連接上數(shù)據(jù)源測試
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping("get")
@Cacheable(cacheNames = "user", key = "#id")
public User getById(Integer id) {
System.out.println("get第一次獲取,不走緩存");
User user = new User();
user.setId(id);
user.setAge(18);
user.setName("benjamin_"+id);
user.setSex(true);
return user;
}
@PostMapping("update")
@CachePut(cacheNames = "user", key = "#search.id")
public User update(@RequestBody User search) {
System.out.println("update更新緩存");
User user = new User();
Integer id = search.getId();
user.setId(id);
user.setAge(search.getAge() != null ? search.getAge()+1 : 0);
user.setName("update_benjamin_"+id);
user.setSex(true);
return user;
}
}
3. 測試
1、調(diào)用查詢接口:localhost:8080/user/get?id=1
2、第一次調(diào)用,打印"get第一次獲取,不走緩存"。再調(diào)用一次發(fā)現(xiàn)沒有打印了,但是數(shù)據(jù)正常查詢,說明走了緩存
3、調(diào)用更新接口
4、再調(diào)用查詢接口,查詢到的就是更新的數(shù)據(jù),說明緩存更新成功
4. 注意事項
謹慎使用maxElementsInMemory
maxElementsInMemory表示的是最大緩存多少個key,這個配置項謹慎使用,一般我們應該根據(jù)占用多少內(nèi)存空間來控制,而不是占用多少個key,如果出現(xiàn)某些key的數(shù)據(jù)量特別大時,就會導致key數(shù)量沒超過,但內(nèi)存占用超過導致的OOM了
這個我們通過一個生成大數(shù)據(jù)量的接口來模擬,其中generateMemoryString
方法可以在文末的源碼倉庫中
1、書寫接口
@GetMapping("build")
@Cacheable(cacheNames = "user", key = "#id")
public User build(Integer id) {
System.out.println("get第一次獲取,不走緩存");
User user = new User();
user.setId(id);
user.setAge(18);
// 生成指定大小的字符串
user.setName(generateMemoryString(id));
user.setSex(true);
return user;
}
2、限制項目JVM內(nèi)存為100m,方便更快模擬出報錯
3、調(diào)用接口localhost:8080/user/build?id=100
,因為該接口會生成大數(shù)據(jù),占用本地緩存,而JVM緩存又給的100M,所以調(diào)用會報錯堆內(nèi)存溢出,如圖所示
4、因此該配置項要謹慎使用,可以通過maxBytesLocalHeap
,maxBytesLocalDisk
設置占用多少內(nèi)存、磁盤來替代
<cache
name="user"
eternal="false"
maxBytesLocalHeap="50M"
maxBytesLocalDisk="200M"
overflowToDisk="false"
diskPersistent="false"
timeToLiveSeconds="3600"
timeToIdleSeconds="0"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
如果maxBytesLocalHeap
和maxElementsInMemory
都配置了的,誰先達到配置的值,就觸發(fā)
如果單個key值太大,仍然會導致OOM
雖然我們上面配置了maxBytesLocalHeap
來限制最大使用的內(nèi)存,比如我們限制了該值為100M,則如果我們有4個30M的數(shù)據(jù)進來,那么就會根據(jù)配置的淘汰策略去淘汰之前的key,以騰出空間來裝新的數(shù)據(jù)
但如果新進來的數(shù)據(jù)很大,比如超過100M了,那么就會一下子裝滿內(nèi)存,甚至淘汰之前的key也不行,所以這種情況下還是會導致OOM的
遇到這種情況,兩種處理辦法,一種是保證不會有大于這個閾值的數(shù)據(jù)產(chǎn)生,這個可以通過業(yè)務代碼控制,二是設置一個全局錯誤捕捉,捕捉產(chǎn)生的OOM報錯,然后返回一個兜底或者其他的狀態(tài)碼,以此標識文章來源:http://www.zghlxwxcb.cn/news/detail-429608.html
演示源碼
https://gitee.com/wuhanxue/wu_study/tree/master/demo/local_cache_demo文章來源地址http://www.zghlxwxcb.cn/news/detail-429608.html
到了這里,關于springboot:緩存不止redis,學會使用本地緩存ehcache的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!