知识要点
- 认识缓存
缓存的定义
缓存的作用
缓存指标
缓存同步方案
缓存穿透和缓存击穿
缓存雪崩 - JSR107规范
- Spring缓存注解
- SpringBoot缓存注解
1.认识缓存
缓存的定义
- 缓存是指可以进行高速数据交换的存储器,使用它可以更快速的操作和访问数据
- 缓存是一个比较大的概念,用来预读取信息(比如硬盘的缓存),或者是暂时存储一些不需要长久保存的信息
特点
- 特点是速度快、临时存放、保存时间短、读操作大于写操作
使用缓存可以避免访问数据库,节省数据库服务器的资源
性能更快,缓存属于内存级的服务器,而DB牵扯到更多的业务逻辑判断和磁盘操作,因此缓存的性能更高
与内存的区别
- 内存是内部存储器,是一个硬件设备
缓存的作用
- 数据访问频率高的地方
- 数据读多写少的情况
- 数据对一致性要求不高的地方
- CPU缓存、操作系统缓存、数据库缓存、JVM编译缓存、CDN缓存、代理与反向代理缓存、前端缓存、应用程序缓存、分布式对象缓存
缓存指标
- 1.命中率:确保通过key快速找到
- 2.过期策略:旧缓存数据需要为新数据腾空间
- 3.缓存键集合的大小:缓存中的每一个对象使用缓存键识别,定位一个对象的唯一方式就是对缓存键执行精准匹配,缓存键越少,命中率越高
- 4.缓存可使用内存空间:缓存可使用空间直接决定了缓存对象的平均大小和缓存对象数量
- 5.缓存对象生存空间:缓存对象生存空间TTL(Time To Live),对象缓存的时间越长,缓存对象被重用的可能性越高
缓存同步方案
- 同步
实时更新,同步调用cache的增删改方法
利用观察者模式/发布订阅/mq等方法进行同步更新 - 异步
缓存设备过期时间,比如一小时
缓存每天定时更新(不设置过期时间)
缓存穿透
- 缓存穿透:是指缓存和数据库都没有的数据,而用户不断发起请求,如发起id为"-1"的数据或id为特别大不存在的数据。这是的用户很有可能是攻击者,攻击会导致数据库压力过大
- 解决方案:
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没啥使用)。这样可以防止攻击用户反复
缓存击穿
- 缓存击穿:是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读取缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
- 设置热点数据永远不过期
- 加互斥锁
缓存雪崩
- 缓存雪崩:是指缓存总数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不用数据都过期了,很多数据都查不到而查数据库
- 解决方案:
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
如果缓存数据是分布式部署,将热点数据均匀分布在不用的缓存数据库中
设置热点数据永远不过期
2.JSR107规范
JSP
- Java Specification Requests的缩写
JCache
- JCache规范定义了一种对Java对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机、失效、各JVM的一致性等,可被用于缓存JSP内最经常读取的数据,如产品目录和价格列表。利用JCACHE,多数查询的反应时间会因为有缓存的数据而加快(内部测试表明反应时间大约快15倍)
JCache
- CachingProvider:定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider
- CacheManager:定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于Cachemanager的上下文中。一个CacheManager仅被一个CachingProvider所拥有
- Cache:一个类似Map的数据结构并临时存储以key为索引的值,一个Cache仅被一个CacheManager所拥有
- Entry:一个存储在Cache中的key-value对
- Expiry:每一个存储在Cache中的条目有一个定义的有效期,一旦超过这个有效期,条目为过期的状态,一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置
3.Spring缓存注解
- Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.Cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR107)注解简化开发
- Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EnCacheCache,ConcurrentMapCache等
- 每次调用需要缓存功能的方法时,Spring会检查指定的目标方法是否已经被调用锅,如果有就直接从缓存中获取方法调用后的结果;如果没有就调用方法并缓存结果返回给用户,下次调用直接从缓存中获取
Class和注解 | 说明 |
---|---|
Cache | 缓存接口,定义缓存操作,实现:RedisCache,EhCacheCache,ConcurrentMapCache等 |
Cache | 缓存接口,定义缓存操作,实现:RedisCache,EhCacheCache,ConcurrentMapCache等 |
CacheManager | 缓存管理器,管理各种缓存组件 |
@Cacheable | 主要针对方法进行配置,根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 确保方法被调用,又希望结果被缓存 |
@EnableCaching | 开启基于注解的缓存 |
KeyGenetator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
Springboot缓存注解
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-stater-cache</artifactId>
</dependency>
- 开启缓存
- 启动器类上配置@EnableCaching
- 指定mapper接口路径@MapperScan
缓存操作方法
-
查询
cacheNames/value:指定缓存组件的名字;
key:缓存数据使用的key,可以指定。默认即使用方法参数的值
condition:指定符合条件的情况下才缓存;如condition=“#id>0” -
更新
修改了数据库的某个数据,同时更新缓存
value:缓存名
key:确保更新的key和已经存在缓存中的key(#id)一致即可做到同时更新数据库数据和缓存中的数据 -
删除
key:指定要清除的数据(对应上key可实现删除数据库和缓存中的数据) -
缓存策略
@Caching:组合组件,可以设置多个缓存策略
SPEL表达式
表达式举例 | 说明 |
---|---|
#root.methodName | 当前被调用的方法名 |
#root.method.name | 当前被调用的方法 |
#root.target | 当前被调用的目标方法 |
#root.targerClass | 当前被调用的目标对象类 |
#root.args[0] | 当前被调用的方法的参数列表 |
@Cacheable(value={“cache1”,“cache2”}) | 当前方法调用使用的缓存列表 |
#p0,#p1 | 方法参数的名字,可以直接#参数名 |
#result | 方法执行后的返回值 |
eg:
pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-stater-cache</artifactId>
</dependency>
Application.java
@SpringBootApplication
@EnableCacheing
public class Application {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
UserServiceImpl.java
...
@Autowired
private UserMapeer userMapper;
@Cacheeable(value="cache”,key="#root.args[0]“)
public User selectByName(String username){
return userMapper.selectByName(username);
}
@Cacheable(value="cache",key="#root.args[0]".contion="#id>5")
public User selectById(Integer id) {
return userMapper.selectById(id);
}
@CachePut(value="cache",key="#root")
public Integer updateUser(User user){
return userMapper.updateUser(user);
}
@CacheEvict(value="cache",key="#id",beforeInvocation=true,allEntries=false)
public Integer deleteById(Integer id){
return userMapper.deleteById(id);
}
...