缓存-SpringCache
1、简介
Spring 从 3.1 开始定义了 org.springframework.cache.Cache
和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术;
并支持使用 JCache(JSR-107)注解简化我们开发;
Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合;
Cache 接 口 下 Spring 提 供 了 各 种 xxxCache 的 实 现 ; 如 RedisCache , EhCacheCache , ConcurrentMapCache 等;
每次调用需要缓存功能的方法时,Spring 会检查检查指定参数的指定的目标方法是否已
经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓
存结果后返回给用户。下次调用直接从缓存中获取。
使用 Spring 缓存抽象时我们需要关注以下两点;
1、确定方法需要被缓存以及他们的缓存策略
2、从缓存中读取之前缓存存储的数据
2、整合&体验@cacheable
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
在application.properties配置使用redis作为缓存
spring.cache.type = redis
在需要缓存的地方加注解@cacheable
@Cacheable({"category"})
@Override
public List<CategoryEntity> getLevel1Categorys() {
List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
return categoryEntities;
}
3、自定义缓存配置
缓存key的值
/*默认行为:
如果缓存中有,方法不会调用
key默认自动生成:缓存的名字::SimpleKey[](自动生成的key值)
缓存的value的值,默认使用jdk序列化机制,将序列化后的数据存到redis
默认ttl时间为-1(永不过期)*/
@Cacheable(value = {"category"},key = "#root.method.name")
@Override
public List<CategoryEntity> getLevel1Categorys() {
List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
return categoryEntities;
}
配置缓存value序列化为json
@EnableConfigurationProperties(CacheProperties.class) @EnableCaching @Configuration public class MyCacheConfig { @Autowired CacheProperties cacheProperties; @Bean RedisCacheConfiguration redisCacheConfiguration () { CacheProperties.Redis redisProperties = cacheProperties.getRedis(); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); //将配置文件中的所有配置都生效 if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } if (redisProperties.getKeyPrefix() != null) { config = config.prefixKeysWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; } }
在application.properies中配置缓存空值防止缓存穿透
#springCache使用缓存的类型
spring.cache.type=redis
#过期时间
spring.cache.redis.time-to-live=600000
#缓存key前缀
#spring.cache.redis.key-prefix=CACHE_
#是否开启使用缓存key前缀,如果指定了前缀就用我们指定的前缀,如果没有就默认使用缓存的名字作为前缀,即@cacheable(value)的value值
spring.cache.redis.use-key-prefix=true
#是否缓存空值。防止缓存穿透
spring.cache.redis.cache-null-values=true
@CacheEvict
失效模式:更新数据库,删除category分区下key为getLevel1Categorys的缓存
@CacheEvict(value = "category",key = "'getLevel1Categorys'") @Override public void updateCascde(CategoryEntity category) { this.updateById(category); categoryBrandRelationService.updateCategory(category.getCatId(), category.getName()); }
删除category分区下的所有数据
@CacheEvict(value = "category",allEntries = true) @Override public void updateCascde(CategoryEntity category) { this.updateById(category); categoryBrandRelationService.updateCategory(category.getCatId(), category.getName()); }
@Caching
同时进行多种缓存操作
举例
@Caching(evict = { @CacheEvict(value = "category",key = "'getLevel1Categorys'"), @CacheEvict(value = "category",key = "'getCatelogJson'") }) // @CacheEvict(value = "category",key = "'getLevel1Categorys'") @Override public void updateCascde(CategoryEntity category) { this.updateById(category); categoryBrandRelationService.updateCategory(category.getCatId(), category.getName()); }
@CachePut
双写模式:更新数据库,也更新缓存
总结
读模式:
- 缓存穿透:查询一个null数据。解决:缓存空数据;ache-null-value=true
- 缓存击穿:大量并发进来同时查询一个正好过期的数据。解决:加锁;@cacheable默认无加锁;可以在@cacheable中加sync = true打开加锁
- 缓存雪崩:大量的key同时过期。解决:加随机时间。加上过期时间即可(因为每个添加缓存的时间点不同,故加上过期时间后自然也不同)。spring.cache.redis.time-to-live = 600000
写模式(缓存与数据库一致性):
- 读写加锁
- 引入Canal中间件,感知到MySQL的更新去更新缓存
- 读多写多,直接去数据库查询就行,不用添加缓存
小结:常规数据(读多写少,即时性,一致性要求不高的数据);完全可以使用Spring-Cache;写模式(只要缓存的数据有过期时间就够了)
特殊数据:特殊设计
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?