目录
1.springboot集成spring cache
本次主要基于redis使用spring cache,进行缓存管理。
首选导入相关依赖(此处未指定版本号,请自行选择):
<!-- redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring cache 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
编写application.yml和application.properties配置文件,配置相关信息 :
spring:
# redis配置连接配置
redis:
database: 0
host: 127.0.0.1
#redis默认端口
port: 6379
password:
timeout: 5000ms
#指定spring-cache为 redis
spring.cache.type=redis
#指定spring-cache过期时间
spring.cache.redis.time-to-live= 60000
#允许缓存空值,避免缓存穿透
spring.cache.redis.cache-null-values=true
创建cache配置类,对redis存储数据进行序列化等操作:
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
public class CacheConfig {
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
//获取redis cache管理器
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
//redis序列化,json格式保存
configuration = configuration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
//使配置文件中生效
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
configuration = configuration.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
configuration = configuration.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
configuration = configuration.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
configuration = configuration.disableKeyPrefix();
}
return configuration;
}
}
然后,我们就可以在业务类中对spring cache进行使用了。
2.@cacheable标签使用
我们可以在对应的业务类中对应的方法上添加 @Cacheable 标签,来使用spring cache缓存。
@Cacheable 常用的主要是以下参数:
- value:分区名;一般一种类型的数据指定统一分区
- key:键值;和redis中的key相同,常用方法名替代
下面对一次查询进行缓存,来进行演示(此处key值为方法名):
@Cacheable(value={"addressCache"},key="#root.methodName")
@Override
public Map<String, List<Address>> searchAllNew() {
//mybatis-plus查询数据库
List<Address> addressList = userAddressMapper.selectList(new QueryWrapper<>());
Map<String,List<Address>> map = new HashMap<>();
map.put("address",addressList);
//自动将返回值缓存进redis中
return map;
}
在spring cache中,会自动查询缓存中是否有该数据:若有,直接返回缓存数据,不执行业务代码;若无,执行业务代码,并将结果缓存进redis中,以便下次查询使用。
3.缓存一致性
关于缓存一致性问题(即数据库中数据和缓存中的数据一致性问题),我们通常通过两种方法来解决:
(1)双写模式:即修改数据时,同时把修改后的数据写入缓存,进行更新。
(2)失效模式:即修改数据时,直接把缓存进行删除。下次查询时,再把新数据存入缓存。
以上两种模式都可能因为网络卡顿等原因,形成脏数据。所以,怎么选择,还需要根据具体的业务情况及数据情况进行。
下面对spring cache中的两种模式进行演示:
(1)使用 @CachePut 标签实现双写模式(此处key值为实体类中的唯一id):
@CachePut(value = {"addressCache"},key = "#address.id")
public Address updateAddress(Address address) {
userAddressMapper.updateById(address);
return userAddressMapper.selectById(address.getId());
}
此处需要注意的是 @CachePut 所标记的方法需要有返回值,否则会报错,无法进行缓存。
(2)使用 @CacheEvict 标签实现失效模式(此处key值为实体类中的唯一id)
@CacheEvict(value = {"addressCache"},key = "#address.id")
public void updateAddress(Address address) {
userAddressMapper.updateById(address);
}
在该模式下,无需返回值,spring cache会自动删除分区下对应的key下的缓存。
4.spring cache的问题
需要注意的是,spring cache默认是不加锁的。因为其是根据标签来进行缓存,无法像我们手动使用redis进行缓存一样,使用redission等工具加上分布式锁。
spring cache提供了一个本地锁(即获取缓存时,给方法加上 synchronized),其使用方式为,在cacheable标签上,添加一个值sync:
//sync,调用redisCacheManage中的加锁(本地锁)的get方法,去查缓存
@Cacheable(value={"addressCache"},key="#root.methodName",sync = true)
尽管如此,spring cache可以一定程度上解决缓存击穿的问题,但也只适用于读多写少(即时性要求不高的数据)的数据。如果读多写多的数据,不建议使用缓存,而是直接查询数据库。
具体缓存如何使用,还需要根据不同的业务要求来进行设计。