1、简介
a. Spring提供的缓存框架;
b. 利用AOP,实现了基于注解的缓存功能;
c. 提供一整套的接口和代码规范、配置、注解等。
2、提供两个接口
a. org.springframework.cache.Cache: 缓存操作的API;
b. org.springframework.cache.CacheManager:提供了各种xxxCache的实现;如RedisCache、EhCacheCache、ConcurrentMapCache等;
3、springboot2.x 整合 SpringCache
a. 添加依赖
org.springframework.boot spring-boot-starter-cache b. 配置文件spring:
cache:
type: redis
c. 启动类开启缓存注解
@EnableCaching
4、SpringCache常用注解
@Cacheable:在方法执行前查看是否有缓存对应的数据,如果有直接返回数据,如果没有调用方法获取数据返回,并缓存起来。
@CacheEvict:将一条或多条数据从缓存中删除。
@CachePut:将方法的返回值放到缓存中。
@EnableCaching:开启缓存注解功能。
@Caching:组合多个缓存注解。
@CacheConfig:统一配置@Cacheable中的value值。
5、KeyGenerator:key生成器
缓存的本质是key-value存储模式,每一次方法的调用都需要生成相应的Key, 才能操作缓存。
通常情况下,@Cacheable有一个属性key可以直接定义缓存key,开发者可以使用SpEL语言定义key值。若没有指定属性key,缓存抽象提供了 KeyGenerator来生成key ,具体源码如下,
由图可看出
如果没有参数,则直接返回SimpleKey.EMPTY;
如果只有一个参数,则直接返回该参数;
若有多个参数,则返回包含多个参数的SimpleKey对象。
当然Spring Cache也考虑到需要自定义Key生成方式,需要我们实现org.springframework.cache.interceptor.KeyGenerator 接口。
自定义Key生成方式:
@Component
public class MyKeyGenerate implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
String s = target.toString()+":"+method.getName()+":"+ Arrays.toString(params);
return s;
}
}
//将myKeyGenerate注入
@Cacheable(cacheNames = "test",keyGenerator = "myKeyGenerate")
public User getUserById(Long id,String username){
User user = new User();
user.setId(id);
user.setUsername(username);
return user;
}
6、必考面试题之缓存击穿、缓存雪崩、缓存穿透
a. 缓存击穿+解决方案
缓存击穿 (某个热点key缓存失效了)
1)缓存中没有但数据库中有的数据,假如是热点数据,那key在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力增大。
2)和缓存雪崩的区别在于这里针对某一key缓存,后者则是很多key。
预防
1)设置热点数据不过期
2)定时任务定时更新缓存
3)设置互斥锁
SpringCache解决方案
1)缓存同步 sync
sync 可以指示底层将缓存锁住,使只有一个线程可以进入计算,而其他线程堵塞,直到返回结果更新到缓存中
@Cacheable(value = {"product"},key = "#root.args[0]", cacheManager = "customCacheManager", sync=true)
b. 缓存雪崩+解决方案
缓存雪崩:大量的Key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
预防
1)存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
2)设置热点数据永远不过期,定时任务定时更新
SpringCache解决方案
1)设置差别的过时时间
2)比如CacheManager配置多个过期时间维度
3)配置文件 time-to-live 配置
cache:
#使用的缓存类型
type: redis
#过时时间
redis:
time-to-live: 3600000
# 开启前缀,默以为true
use-key-prefix: true
# 键的前缀,默认就是缓存名cacheNames
key-prefix: XD_CACHE
# 是否缓存空结果,防止缓存穿透,默以为true
cache-null-values: true
c. 缓存穿透+解决方案
缓存穿透:(查询不存在数据)
查询一个不存在的数据,由于缓存是不命中的,并且出于容错考虑,如发起为id为“-1”不存在的数据
如果从存储层查不到数据则不写入缓存这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。存在大量查询不存在的数据,可能DB就挂掉了,这也是黑客利用不存在的key频繁攻击应用的一种方式。
预防
接口层增加校验,数据合理性校验
缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,设置短点的过期时间,防止同个key被一直攻击
SpringCache解决方案
空结果也缓存,默认不配置condition或者unless就行
cache:
#使用的缓存类型
type: redis
#过时时间
redis:
time-to-live: 3600000
# 开启前缀,默以为true
use-key-prefix: true
# 键的前缀,默认就是缓存名cacheNames
key-prefix: XD_CACHE
# 是否缓存空结果,防止缓存穿透,默以为true
cache-null-values: true