天天看點

利用Spring Cache提升系統性能,緩存助力加速

作者:編碼是個技術活
利用Spring Cache提升系統性能,緩存助力加速

引言

首先SpringCache 并非某一種 Cache 實作的技術,Spring Cache是Spring架構提供的一個子產品,SpringCache 是一種緩存實作的通用技術,基于 Spring 提供的 Cache 架構,讓開發者更容易将自己的緩存實作高效便捷的嵌入到自己的項目中。

它利用了AOP,實作了基于注解的緩存功能,并且進行了合理的抽象,業務代碼不用關心底層是使用了什麼緩存架構,隻需要簡單地加一個注解,就能實作緩存功能了。而且Spring Cache也提供了很多預設的配置,可以快速的上手使用緩存功能。

為什麼使用Spring Cache

我們發現網上有各種緩存架構,各有各的優勢,比如Redis、Memcached、Guava、Caffeine等等。

如果我們的程式想要使用緩存,就要與這些架構耦合。聰明的架構師已經在利用接口來降低耦合了,利用面向對象的抽象和多态的特性,做到業務代碼與具體的架構分離。

但我們仍然需要顯式地在代碼中去調用與緩存有關的接口和方法,在合适的時候插入資料到緩存裡,在合适的時候從緩存中讀取資料。但如果使用spring Cache能給我們帶來如下好處:

1)簡化緩存管理:Spring Cache提供了一套簡單且統一的API和注解,使得緩存的添加、更新、失效等操作變得更加友善和易于管理。

2)降低對底層緩存實作的依賴:Spring Cache的抽象層幫助開發人員擺脫對具體緩存實作的綁定,靈活地切換不同的緩存提供者而不需要改動業務代碼。

3)支援多種緩存實作:Spring Cache相容多種主流的緩存提供者,如Ehcache、Redis、Guava等,可以根據需求選擇适合的緩存實作方式。

4)增強代碼可讀性:通過在方法上添加緩存注解,可以清晰地辨別出哪些方法會使用緩存,提高代碼的可讀性和可維護性。

如何使用Spring Cache

spring cache的使用非常簡單,主要三步驟:加依賴,開啟緩存,加緩存注解。

導入相關依賴

在pom.xml檔案中添加Spring Cache以及具體緩存實作(這裡以Ehcache為例)的依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>           

配置Spring Cache

在Spring Boot應用中,可以通過在application.properties或application.yml中進行相關配置,如:

spring.cache.type=ehcache           

編寫Service類

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class MyDataService {

    @Cacheable(value = "myDataCache", key = "#id")
    public String getDataById(Long id) {
        System.out.println("Fetching data from database for id: " + id);
        return "Data" + id;
    }
}           

調用Service方法

MyDataService類中的getDataById方法通過@Cacheable注解指定了緩存的名稱為myDataCache,并指定了緩存的key為方法的參數id。當調用該方法時,如果緩存中存在對應key的資料,将直接從緩存中取出傳回;否則會執行方法内部邏輯,并将結果放入緩存中。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyDataController {

    @Autowired
    private MyDataService myDataService;

    @GetMapping("/data/{id}")
    public String getData(@PathVariable Long id) {
        return myDataService.getDataById(id);
    }
}           

Spring Cache常用注解

Spring Cache提供了幾個常用的注解,分别為@Cacheable、@CachePut、@CacheEvict、@Caching、 @CacheConfig。除了最後一個CacheConfig外,其餘四個都可以用在類上或者方法級别上,如果用在類上,就是對該類的所有public方法生效,下面分别介紹一下這幾個注解。

@Cacheable:

@Cacheble注解表示這個方法有了緩存的功能,方法的傳回值會被緩存下來,下一次調用該方法前,會去檢查是否緩存中已經有值,如果有就直接傳回,不調用方法。如果沒有,就調用方法,然後把結果緩存起來。。

參數:

value :用來指定緩存元件的名字
key :緩存資料時使用的 key,可以用它來指定。預設是使用方法參數的值。(這個 key 你可以使用 spEL 表達式來編寫)
keyGenerator :key 的生成器。 key 和 keyGenerator 二選一使用
cacheManager :可以用來指定緩存管理器。從哪個緩存管理器裡面擷取緩存。
condition :可以用來指定符合條件的情況下才緩存
unless :否定緩存。當 unless 指定的條件為 true ,方法的傳回值就不會被緩存。當然你也可以擷取到結果進行判斷。(通過 #result 擷取方法結果)
sync :是否使用異步模式。           

@CachePut:

加了@CachePut注解的方法,會把方法的傳回值put到緩存裡面緩存起來,供其它地方使用。。

參數:

value:指定緩存的名稱,表示資料将被存儲在哪個緩存中。這個參數是必須要指定的。
key:指定緩存項的key,可以使用SpEL表達式來動态生成key。如果不指定,則預設使用方法的所有參數作為key。
condition:指定一個SpEL表達式,隻有當條件為true時才會将傳回值存儲到緩存中。預設為空字元串,表示始終存儲到緩存。
unless:指定一個SpEL表達式,當條件為true時不會将傳回值存儲到緩存中。通常用于排除特定情況下的緩存操作。
keyGenerator:指定自定義的KeyGenerator實作類,用于生成緩存key。
cacheManager:指定使用的緩存管理器的名稱,用于從多個緩存管理器中選擇特定的一個。           

@CacheEvict

使用了CacheEvict注解的方法,會清空指定緩存。。

參數:

value:指定緩存的名稱,表示要從哪個緩存中移除資料。這個參數是必須要指定的。
key:指定要移除的緩存項的key,可以使用SpEL表達式來動态生成key。如果不指定,則預設使用方法的所有參數作為key。
condition:指定一個SpEL表達式,隻有當條件為true時才會執行緩存清除操作。預設為空字元串,表示始終清除緩存。
allEntries:指定是否移除緩存中所有的資料(預設為false)。設為true時将移除緩存中的所有資料,忽略key參數。
beforeInvocation:指定在方法執行之前還是之後執行緩存清除操作(預設為false)。設為true時,在方法執行之前就會清除緩存;設為false時,在方法執行成功後再清除緩存。           

@Caching

Java注解的機制決定了,一個方法上隻能有一個相同的注解生效。那有時候可能一個方法會操作多個緩存(這個在删除緩存操作中比較常見,在添加操作中不太常見)。

通過@Caching注解,開發者可以将多個緩存操作注解組合在一起,實作複雜的緩存邏輯。例如,在某個方法中可能既需要查詢緩存又要更新緩存,或者需要在特定條件下清除緩存,這時可以使用@Caching注解來進行統一管理。這樣可以減少重複代碼、提高代碼可讀性,并靈活地控制緩存操作的行為。

參數:

cacheable:指定一個或多個@Cacheable注解,表示在方法執行前檢查緩存中是否存在結果,并在緩存命中時直接傳回緩存資料。
put:指定一個或多個@CachePut注解,表示無論緩存中是否存在相同key的資料,都會執行方法并更新緩存中對應的值。
evict:指定一個或多個@CacheEvict注解,表示從緩存中移除一個或多個緩存項。           

Spring Cache內建Redis

利用Spring Cache提升系統性能,緩存助力加速

導入相關依賴

從官方文檔可以看到spring-boot-starter-data-redis 已經包含了jedis用戶端,我們在使用jedis連接配接池的時候不必再添加jedis依賴。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>           
利用Spring Cache提升系統性能,緩存助力加速

添加redis相關配置

server:
  port: 8080
spring:
  # redis相關配置
  redis:
    database: 0
    host: localhost
    port: 6379
    password: 123456
    jedis:
      pool:
        # 連接配接池最大連接配接數(使用負值表示沒有限制)
        max-active: 8
        # 連接配接池最大阻塞等待時間(使用負值表示沒有限制)
        max-wait: -1ms
        # 連接配接池中的最大空閑連接配接
        max-idle: 8
        # 連接配接池中的最小空閑連接配接
        min-idle: 0
    # 連接配接逾時時間(毫秒)預設是2000ms
    timeout: 2000ms
  cache:
    redis:
      ## Entry expiration in milliseconds. By default the entries never expire.
      time-to-live: 1d
      #寫入redis時是否使用鍵字首。
      use-key-prefix: true           

啟用緩存功能

在Spring Boot的主應用程式類上添加@EnableCaching注解,啟用緩存功能。

編寫Service類

建立一個Service類,定義一個簡單的方法進行資料查詢,并添加緩存注解使用Redis作為緩存提供者

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class MyDataService {

    @Cacheable(value = "myDataCache", key = "#id")
    public String getDataById(Long id) {
        System.out.println("Fetching data from database for id: " + id);
        return "Data" + id;
    }
}           

示例Controller類: 建立一個Controller類,調用Service方法擷取資料。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyDataController {

    @Autowired
    private MyDataService myDataService;

    @GetMapping("/data/{id}")
    public String getData(@PathVariable Long id) {
        return myDataService.getDataById(id);
    }
}           

總結

Spring Cache是Spring架構提供的緩存抽象層,用于簡化應用中對緩存的管理。通過使用Spring Cache,開發者可以輕松地将緩存內建到應用程式中,提高性能、降低資料庫負載,并優化系統的響應時間,但也存在以下不足:

  • 不支援TTL,不能為每個 key 設定單獨過期時間 expires time,
  • 針對多線程沒有專門的處理,是以當多線程時,是會産生資料不一緻性的。(同樣,一般有高并發操作的緩存資料,都會特殊處理,而不太使用這種方式)