Spring Boot 緩存應(yīng)用場(chǎng)景
1. 前言
緩存是性能提升的大殺器!
要知道,內(nèi)存的讀寫速度是硬盤的幾十倍到上百倍。緩存實(shí)際上就是利用內(nèi)存的高速讀寫特性,提高熱點(diǎn)數(shù)據(jù)的操作速度。
Spring Boot 中使用緩存非常簡(jiǎn)單,并且支持多種緩存實(shí)現(xiàn)。
本篇介紹比較常用的幾種緩存實(shí)現(xiàn)方式,及其對(duì)應(yīng)的應(yīng)用場(chǎng)景。
2. Spring Boot 默認(rèn)緩存
Spring Boot 默認(rèn)緩存是基于 ConcurrenMapCacheManager 緩存管理器實(shí)現(xiàn)的,從這個(gè)類名就能發(fā)現(xiàn)它本質(zhì)上應(yīng)該是一個(gè) Map 集合容器。
ConcurrenMapCacheManager 結(jié)構(gòu)比較簡(jiǎn)單,一般用于比較輕量級(jí)的緩存使用場(chǎng)景。也就是緩存的數(shù)據(jù)量比較小,緩存操作不是特別頻繁的場(chǎng)景。
接下來(lái)就具體演示下, Spring Boot 默認(rèn)緩存實(shí)現(xiàn)過(guò)程。
2.1 使用 Spring Initializr 創(chuàng)建項(xiàng)目
Spring Boot 版本選擇 2.2.5 ,Group 為 com.imooc , Artifact 為 spring-boot-cache ,生成項(xiàng)目后導(dǎo)入 Eclipse 開(kāi)發(fā)環(huán)境。
2.2 引入項(xiàng)目依賴
引入 Web 項(xiàng)目依賴和緩存依賴。
實(shí)例:
<!-- Web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 緩存依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.3 開(kāi)啟緩存
在啟動(dòng)類上添加注解 @EnableCaching 開(kāi)啟緩存功能。
實(shí)例:
@SpringBootApplication
@EnableCaching // 開(kāi)啟緩存
public class SpringBootCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootCacheApplication.class, args);
}
}
2.4 定義服務(wù)層方法
正常服務(wù)層方法會(huì)調(diào)用數(shù)據(jù)訪問(wèn)層方法訪問(wèn)數(shù)據(jù)庫(kù),此處我們只需要演示緩存的作用,所以打印日志代替數(shù)據(jù)庫(kù)訪問(wèn)方法。
實(shí)例:
/**
* 商品服務(wù)類
*/
@Service
@CacheConfig(cacheNames = "GoodsCache")
public class GoodsService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 按id獲取商品信息
*/
@Cacheable
public GoodsDo getById(Long id) {
logger.info("getById({})", id);
GoodsDo goods = new GoodsDo();
goods.setId(id);
goods.setName("goods-" + id);
return goods;
}
/**
* 刪除商品
*/
@CacheEvict(key = "#id")
public void remove(Long id) {
logger.info("remove({})", id);
}
/**
* 編輯商品信息
*/
@CachePut(key = "#goods.id")
public GoodsDo edit(GoodsDo goods) {
logger.info("edit id:{}", goods.getId());
return goods;
}
}
對(duì)于使用緩存的 GoodsService 服務(wù)類,我們需要具體解釋下:
@CacheConfig
注解用于指定本類中方法使用的緩存名稱,該類使用的緩存名稱為 GoodsCache ,與其他緩存區(qū)域是隔離的。@Cacheable
用于開(kāi)啟方法緩存,緩存的鍵是方法的參數(shù),緩存的值是方法的返回值。如果多次調(diào)用該方法時(shí)參數(shù) id 值相同,則第一次會(huì)執(zhí)行方法體,并將返回值放入緩存;后續(xù)方法不會(huì)再執(zhí)行方法體,直接將緩存的值返回。@CachePut
可以更新緩存,key = "#id"
表示采用參數(shù)中的 id 屬性作為鍵。當(dāng)緩存中該鍵的值不存在時(shí),則將返回值放入緩存;當(dāng)緩存中該鍵的值已存在時(shí),會(huì)更新緩存的內(nèi)容。@CacheEvict
可以移除緩存,當(dāng)調(diào)用該方法時(shí),會(huì)移除 goods 中 id 屬性對(duì)應(yīng)的緩存內(nèi)容。
2.5 測(cè)試
為了充分理解緩存的含義,我們通過(guò)測(cè)試類發(fā)起測(cè)試。
實(shí)例:
@SpringBootTest
class SpringBootCacheApplicationTests {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private CacheManager cacheManager;
@Autowired
private GoodsService goodsService;
// 顯示當(dāng)前使用的緩存管理器類型
@Test
void showCacheManager() {
// 輸出:org.springframework.cache.concurrent.ConcurrentMapCacheManager
logger.info(cacheManager.getClass().toString());
}
// 緩存測(cè)試
@Test
void cacheTest() {
// 第一次執(zhí)行,沒(méi)有緩存,執(zhí)行方法體
goodsService.getById(1L);
// 再次執(zhí)行,直接取出緩存,不執(zhí)行方法體
goodsService.getById(1L);
// 移除緩存
goodsService.remove(1L);
// 再次執(zhí)行,已經(jīng)沒(méi)有對(duì)應(yīng)緩存,所以執(zhí)行方法體
GoodsDo oldGoods = goodsService.getById(1L);
// 打印緩存內(nèi)容
logger.info("old goods id:{} name:{}", oldGoods.getId(), oldGoods.getName());
// 更新緩存
GoodsDo temp = new GoodsDo();
temp.setId(1L);
temp.setName("新的商品");
goodsService.edit(temp);
// 查詢并打印已更新的緩存內(nèi)容
GoodsDo newGoods = goodsService.getById(1L);
logger.info("new goods id:{} name:{}", newGoods.getId(), newGoods.getName());
}
}
我們查看下控制臺(tái)輸出如下,驗(yàn)證了我們?cè)O(shè)計(jì)的緩存機(jī)制。
3. 使用 Ehcache 緩存
Spring Boot 默認(rèn)的緩存實(shí)現(xiàn)比較簡(jiǎn)單,功能也十分有限。如果是企業(yè)級(jí)的中大型應(yīng)用,需要尋求更加穩(wěn)定、可靠的緩存框架。
Ehcache 是 Java 編程領(lǐng)域非常著名的緩存框架,具備兩級(jí)緩存數(shù)據(jù)——內(nèi)存和磁盤,因此不必?fù)?dān)心內(nèi)存容量問(wèn)題。另外 Ehcache 緩存的數(shù)據(jù)會(huì)在 JVM 重啟時(shí)自動(dòng)加載,不必?fù)?dān)心斷電丟失緩存的問(wèn)題。
總之 Ehcache 的功能完整性和運(yùn)行穩(wěn)定性遠(yuǎn)遠(yuǎn)強(qiáng)于 Spring Boot 默認(rèn)的緩存實(shí)現(xiàn)方式,而且 Spring Boot 使用 Ehcache 非常便捷,接下來(lái)我們就來(lái)實(shí)現(xiàn)下。
3.1 添加 Ehcache 依賴
我們?cè)?spring-boot-cache 項(xiàng)目的基礎(chǔ)上添加 Ehcache 依賴。
實(shí)例:
<!-- Ehcache 依賴 -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!-- cache-api 依賴 -->
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
3.2 添加 Ehcache 配置文件
首先在 application.properties 中指定配置文件的位置。
實(shí)例:
spring.cache.jcache.config=classpath:ehcache.xml
spring.cache.type=jcache
然后在 resource 文件夾中添加 ehcache.xml 配置文件,內(nèi)容如下:
實(shí)例:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd">
<!-- 持久化路徑 -->
<persistence directory="C://ehcache" />
<!--緩存模板 -->
<cache-template name="CacheTemplate">
<expiry>
<!--存活時(shí)間 -->
<tti>60</tti>
</expiry>
<resources>
<!--堆空間 -->
<heap unit="entries">2000</heap>
<!-- 堆外空間 -->
<offheap unit="MB">500</offheap>
</resources>
</cache-template>
<!--緩存對(duì)象 -->
<cache alias="GoodsCache" uses-template="CacheTemplate">
</cache>
</config>
Tips:Ehcache 的配置比較復(fù)雜,此處只是給出簡(jiǎn)單的示例,感興趣的同學(xué)可以查閱更多資料。
3.3 測(cè)試
由于之前已經(jīng)在啟動(dòng)類添加 @EnableCaching ,我們?cè)俅芜\(yùn)行測(cè)試類,輸出結(jié)果如下。
注意控制臺(tái)出現(xiàn)了 EhcacheManager 的字樣,說(shuō)明我們此時(shí)使用的緩存是 Ehcache 。
4. 使用 Redis 緩存
Ehcache 依然是 Java 進(jìn)程內(nèi)的緩存框架,受限于 JVM 整體的內(nèi)存分配策略。
如果是大型系統(tǒng),緩存的數(shù)據(jù)量特別大,且性能要求很高,可以考慮直接使用 Redis 作為緩存。
Redis 可以采用單機(jī)、主備、集群等模式,視乎具體項(xiàng)目需求決定即可。目前各大云計(jì)算廠商均提供商用版的 Redis 緩存服務(wù),性能卓越且接入簡(jiǎn)單快速。
本節(jié)簡(jiǎn)單地演示 Spring Boot 中使用 Redis 單機(jī)緩存的方法,真實(shí)生產(chǎn)環(huán)境中建議至少使用主備類型的 Redis 實(shí)例。
4.1 修改緩存依賴
因?yàn)樾枰褂?Redis 緩存,所以將引入的依賴項(xiàng)修改如下:
實(shí)例:
<!-- Web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 緩存依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Redis 相關(guān)依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
4.2 修改緩存配置
修改 application.properties 配置文件,將 Redis 配置及緩存配置設(shè)置如下:
實(shí)例:
# 過(guò)期時(shí)間
spring.cache.redis.time-to-live=6000s
# Redis庫(kù)的編號(hào)
spring.redis.database=0
# Redis實(shí)例地址
spring.redis.host=127.0.0.1
# Redis實(shí)例端口號(hào),默認(rèn)6379
spring.redis.port=6379
# Redis登錄密碼
spring.redis.password=Easy@0122
# Redis連接池最大連接數(shù)
spring.redis.jedis.pool.max-active=10
# Redis連接池最大空閑連接數(shù)
spring.redis.jedis.pool.max-idle=10
# Redis連接池最小空閑連接數(shù)
spring.redis.jedis.pool.min-idle=0
4.3 測(cè)試
由于之前已經(jīng)通過(guò)注解 @EnableCaching 開(kāi)啟了緩存功能,此時(shí)我們直接運(yùn)行測(cè)試類進(jìn)行測(cè)試,輸出結(jié)果如下:
從上圖輸出結(jié)果可以看出,已經(jīng)成功使用了 Redis 緩存管理器。
另外我們可以直接使用 Redis 客戶端查看生成的緩存信息,如下圖已經(jīng)有名為 GoodsCache::1
的緩存鍵存在了。
5. 小結(jié)
Spring Boot 支持多種緩存實(shí)現(xiàn)方式,可以根據(jù)項(xiàng)目需求靈活選擇。
- 緩存數(shù)據(jù)量較小的項(xiàng)目,可以使用 Spring Boot 默認(rèn)緩存。
- 緩存數(shù)據(jù)量較大的項(xiàng)目,可以考慮使用 Ehcache 緩存框架。
- 如果是大型系統(tǒng),對(duì)緩存的依賴性比較高,還是建議采用獨(dú)立的緩存組件 Redis ,通過(guò)主備、集群等形式提高緩存服務(wù)的性能和穩(wěn)定性。