在使用缓存前我们首先需要知道,为什么需要使用缓存。缓存能够给我带来什么解决什么问题。是否所有的数据都可以使用缓存。
1.缓存是什么?
缓存是一种存储数据的组件,它可以是硬件或软件,其目的是为了加快数据检索的速度,
减少对原始数据源的访问次数,从而提高整体性能和响应速度。缓存通常存储最近
或频繁访问的数据,这样当下次需要这些数据时,可以直接从缓存中快速获取,
而不必再次从较慢的源头(如硬盘、数据库或远程服务器)检索。从而提升软件的性能。向常见的有
浏览器缓存,CPU高速缓存,应用缓存,cdn缓存,数据库缓存等。
我们知道了缓存可以提高我们系统的访问速度,但是缓存也会带来一些问题。最常见的就是数据一致性问题,当然还有其他的问题。但是在利与弊的权衡下,牺牲一些小的问题(当然这个也要根据实际的常见和具体方案,并不能一概而论的说能否接收这些小的数据不一致问题)来提供系统的性能和延迟等问题,缓存还是有必要加入的。
下面注意接收下缓存在项目中的一些使用方式:
1.集成redis实现缓存
这个上一个章节写了一个简单登录模块介绍过,这次在重新介绍下
1. 在项目中引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置文件中配置相关的参数:
#redis配置
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
3.配置redis的模板(当然你不配置也是可以的)
@Configuration
public class RedisConfig {
@Bean("redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
private RedisSerializer<Object> valueSerializer() {
//序列化所有类包括jdk提供的
ObjectMapper objectMapper = new ObjectMapper();
//设置序列化的域(属性,方法etc)以及修饰范围,Any包括private,public 默认是public的
//ALL所有方位,ANY所有修饰符
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//enableDefaultTyping 原来的方法存在漏洞,2.0后改用如下配置
//指定输入的类型
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL);
//如果java.time包下Json报错,添加如下两行代码
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(new JavaTimeModule());
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(objectMapper,Object.class);
return serializer;
}
}
3.使用测试
2.redis集成spring cache实现缓存
1.引入依赖:
在项目的pom.xml文件中添加Spring Boot的缓存启动器和Redis的依赖项
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- 配置文件配置redis 参数(同上面的配置)
3.启用缓存:
在Spring Boot的主类上使用@EnableCaching注解来启用缓存支持
4.配置缓存管理器
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return new RedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
//// 默认过期时间30分钟
this.getRedisCacheConfigurationWithTtl(30),
// 指定特定缓存的配置
this.getRedisCacheConfigurationMap()
);
}
private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
// 在这里配置特定缓存的过期时间和序列化方式
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
// 示例:为名为"myCache"的缓存设置60分钟的过期时间
redisCacheConfigurationMap.put("myCache", this.getRedisCacheConfigurationWithTtl(60));
return redisCacheConfigurationMap;
}
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer minutes) {
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(om,Object.class);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)
).prefixCacheNameWith("yulang-")
.entryTtl(Duration.ofMinutes(minutes));
return redisCacheConfiguration;
}
}
5.使用注解方式使用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
//获取缓存
@Cacheable(value = "myCache", key = "#userId")
public User findMyObjectById(String userId) {
// 这里执行数据库查询或其他操作
return userMapper.findById(userId);
}
// 添加缓存
@CachePut(value = "myCache", key = "#myObject.userId")
public User updateMyObject(User myObject) {
// 更新对象的操作
return userMapper.save(myObject);
}
// 删除缓存
@CacheEvict(value = "myCache", key = "#userId")
public void deleteMyObject(String userId) {
// 删除对象的操作
userMapper.deleteById(userId);
}
}
@Autowired
private UserService userService;
@PostMapping("/save")
public void save() {
User user = new User();
user.setUserId("9999");
user.setUserName("999");
userService.updateMyObject(user);
}
@PostMapping("/get")
public Object get() {
return userService.findMyObjectById("9999");
}
@PostMapping("/delete")
public void delete() {
userService.deleteMyObject("9999");
}
测试:
1.调用 /save 接口,缓存中会写入数据
2。调用 /get, 方法验证是否走缓存(观察是否走的查询)
- 删除 /delete,再次查询,缓存中没有数据,走的查询
总结两种的cache方式,都可以在实际的项目中使用。其实用哪一种都可以,关键点在于是否用的方便顺心。我们在实际的项目中其实两种结合起来使用过。在实际应用中,应根据具体的业务需求和系统特点来选择合适的集成方式。
第一种:编程式的方式在于灵活多变,可以更新实际的需求在不同的位置位置进行使用
优点
- 直接操作Redis:可以使用RedisTemplate直接操作Redis,执行各种Redis命令,灵活性更高。
- 性能较好:没有JCache API的额外开销,性能相对更好一些。
缺点
- 代码侵入性强:需要在业务代码中显式调用RedisTemplate,代码侵入性强,不易维护。
- 缺乏统一的缓存抽象:不同的缓存实现差异较大,不利于缓存的切换和统一管理
第二种: 申明式调用方式,在于使用简单方便,代码冗余度低,可以搭配其他的缓存框架使用。
优点
- 统一的缓存抽象:JCache(JSR-107)提供了一个统一的缓存管理接口,使得缓存的使用和切换变得更加灵活3。
- 注解支持:可以使用@Cacheable、@CachePut、@CacheEvict等注解来声明缓存逻辑,简化代码编写。
- 缓存抽象与实现分离:开发者可以专注于缓存的使用,而不需要关心具体的缓存实现,便于后期维护和升级。
缺点
- 性能开销:JCache API在使用时会有一些性能开销,因为它需要通过代理模式来拦截方法调用。
- 配置复杂:对于复杂的缓存配置,如自定义序列化方式、过期策略等,配置可能会比较繁琐