您现在的位置是:首页 >学无止境 >什么是Spring Cache?Spring项目如何使用?网站首页学无止境
什么是Spring Cache?Spring项目如何使用?
前言
目前Spring Cloud微服务在Web项目中占据了主流地位,如果去面试关于Spring Cloud的岗位时,面试官一般都会提问你的项目是如何优化的,从哪些方面入手去优化。而缓存技术绝对是项目必不可少的,所以我们必须掌握好Java项目的缓存技术。
目前,在 Java 中,常见的缓存有以下几种:
- JVM 缓存:JVM 缓存是指 Java 虚拟机为类加载器所创建的缓存。Java 虚拟机通过缓存已经加载的类来提高应用程序的性能。
- 数据库缓存:数据库缓存是指将查询结果缓存在内存中,以便下次查询时可以直接从内存中获取,避免了重复查询数据库的操作。
- Web 缓存:Web 缓存是指将 Web 页面、图片等静态资源缓存到本地,以便下次访问时可以直接从本地获取,提高 Web 应用程序的性能。
- 分布式缓存:分布式缓存是指将数据缓存到多个节点上,以便多个应用程序可以共享缓存,提高系统的性能和可伸缩性。
- 本地缓存:本地缓存是指将数据存储在应用程序本地的内存或磁盘上,以便快速读取和更新数据,避免了频繁访问远程资源的开销。
- Spring 缓存:Spring 框架提供了一套缓存框架,可以将方法调用的结果缓存到内存或磁盘中,以便下次调用时可以直接从缓存中获取结果,提高应用程序的性能。
今天我主要讲解Spring 缓存,因为Spring cache简单、方便、效率高,绝对是Spring Cloud微服务项目的最佳选择
一、什么是Spring Cache
Spring Cache是Spring框架中的一个模块,它提供了一种简单的方法来缓存数据和对象。通过使用Spring Cache,开发人员可以将常用的数据和对象存储在内存中,以提高应用程序的性能和响应速度。
Spring Cache支持多种缓存实现,包括内存、Redis、Memcached等。开发人员可以根据自己的需求选择合适的缓存实现。
Spring Cache还提供了一些高级功能,如缓存预热、缓存过期、分布式缓存等,使得开发人员可以更加灵活地使用缓存。同时,Spring Cache还与Spring框架的其他模块集成良好,可以方便地与其他组件一起使用。
1、优点
-
提高性能:通过将常用的数据和对象存储在内存中,Spring Cache可以大大提高应用程序的性能和响应速度。
-
简化代码:Spring Cache提供了一些高级功能,如缓存预热、缓存过期和分布式缓存等,使得开发人员可以更加灵活地使用缓存,从而简化了代码。
-
促进团队协作:Spring Cache可以促进团队协作,因为它可以在多个开发人员之间共享缓存数据,从而减少了重复的工作和代码冗余。
-
实现分布式系统:Spring Cache可以与Spring框架的其他模块集成良好,可以方便地与其他组件一起使用,从而实现分布式系统。
-
支持多种缓存实现:Spring Cache支持多种缓存实现,包括内存、Redis、Memcached等,开发人员可以根据自己的需求选择合适的缓存实现。
-
提供基本的Cache抽象:方便切换各种底层Cache,通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成。
-
提供事务回滚时也自动回滚缓存:支持比较复杂的缓存逻辑。
-
通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成。
2、缺点
在使用 Spring Cache 时,可能会遇到一些不足之处。下面是一些可能的不足:
- 读模式下缓存失效的问题:在 Spring Cache 中,缓存的读模式下可能会出现缓存失效的问题。这是因为在读模式下,缓存的数据可能会过期,导致缓存失效。为了解决这个问题,可以使用 Spring Cache 提供的读写分离功能,将读请求和写请求分别发送到不同的 Cache 实例。
- 缓存穿透:在某些情况下,缓存穿透可能会发生。这是因为在读模式下,缓存的数据可能会被其他组件重新读取,导致缓存穿透。为了避免缓存穿透,可以使用 Spring Cache 提供的写模式,并设置 cache-null-values: true 来允许写入空值。
- 缓存击穿:在大量并发请求的情况下,可能会出现缓存击穿的问题。这是因为在这种情况下,同时有大量的请求查询一个正好过期的数据,导致请求全部转发到数据库,从而导致数据库压力过大。为了避免缓存击穿,可以设置缓存的过期时间,或者使用其它缓存框架,如 Ehcache 或 RedisCache。
- 缓存雪崩:在某些情况下,缓存的大量键过期时间相同,导致某一刻缓存同时失效,请求全部转发到数据库。为了避免缓存雪崩,可以使用 Spring Cache 提供的键值哈希策略,将键值哈希到不同的桶中,避免键值过期时间相同的问题。
- 总的来说,Spring Cache 在处理读模式下缓存失效、缓存穿透、缓存击穿和缓存雪崩等问题方面表现良好,但在写模式下需要使用其他方式进行处理。使用 Spring Cache 可以轻松地管理和优化应用程序的缓存。
二、Spring Cache原理
1、基本流程图
2、原理
当我们在Spring中使用缓存时,会按照以下步骤进行操作:
-
首先,Spring会检查是否使用了缓存注解。如果使用了,那么会在缓存的缓存管理器中查找是否有已经缓存的数据。
-
如果缓存管理器中没有对应的缓存数据,那么会执行方法体中的代码,并将结果缓存起来。
-
如果缓存管理器中已经存在缓存数据,那么直接返回缓存数据。
当我们在更新数据时,会按照以下步骤进行操作:
-
首先,更新方法会执行,然后会更新数据库中的数据。
-
然后,Spring会在缓存管理器中查找是否存在已经缓存的数据。
-
如果存在缓存数据,那么会将缓存数据删除。
-
下一次查询时,会重新执行方法体中的代码,并将新的结果缓存起来。
以上就是Spring Cache的工作原理。
三、Spring Cache具体实现与应用
1、引入SpringCache依赖
- .引入SpringCache依赖
<!--Spring Cache,使用注解简化开发-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
-.引入redis依赖
<!--redis启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、属性配置
spring:
redis:
host: 192.168.56.10
port: 6379
cache:
type: redis # 使用redis作为缓存
redis:
time-to-live: 3600s # 过期时间
# key-prefix: CACHE_ # 会导致自己在@Cacheable里设置的名字失效,所以这里不指定
use-key-prefix: true # key值加前缀
cache-null-values: true # 缓存控制
3、类配置
@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
@Configuration
public class MyCacheConfig {
// @Autowired
// CacheProperties cacheProperties;
/**
* 需要将配置文件中的配置设置上
* 1、使配置类生效
* 1)开启配置类与属性绑定功能EnableConfigurationProperties
*
* @ConfigurationProperties(prefix = "spring.cache") public class CacheProperties
* 2)注入就可以使用了
* @Autowired CacheProperties cacheProperties;
* 3)直接在方法参数上加入属性参数redisCacheConfiguration(CacheProperties redisProperties)
* 自动从IOC容器中找
* <p>
* 2、给config设置上
*/
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//序列化key,不变
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
//序列化值,使用json
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
// 当自己往IOC注入了RedisCacheConfiguration配置类时,以下参数全都失效,需要手动设置
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
4、查询时读取缓存
@Cacheable(value = {"category"}, key = "#root.method.name", sync = true)
@Override
public List<CategoryEntity> getLevel1Categorys() {
System.out.println("调用了getLevel1Categorys...");
// 查询父id=0
return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
}
5、更新时读取缓存
@CacheEvict(value = {"category"}, allEntries = true) //删除category分区里所有的缓存
@Transactional
@Override
public void updateCascade(CategoryEntity category) {
this.updateById(category);
if (!StringUtils.isEmpty(category.getName())) {
// 更新冗余表
categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
// TODO 更新其他冗余表
}
}
6、双写模式
/**
* 级联更新
* 缓存策略:双写模式,方法执行完更新缓存
*/
@CachePut(value = "category", key = "'level1Categorys'")
@Transactional
@Override
public void updateCascade(CategoryEntity category) {
this.updateById(category);
if (!StringUtils.isEmpty(category.getName())) {
// 更新冗余表
categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
// TODO 更新其他冗余表
}
}
7、注解解释:
@Cacheable:更新缓存【读操作:如果当前缓存存在方法不被执行,不存在则执行get方法并更新缓存】
@CacheEvict:删除缓存【写操作:失效模式,方法执行完删除缓存】
@CachePut:更新缓存【写操作:双写模式,方法执行完更新缓存】
@Caching:组合以上多个缓存操作
@CacheConfig:在类级别共享缓存的相同配置
源码下载:
gitee.com/charlinchenlin/koo-erp