为什么使用缓存 ?
因为我们的热点数据也就是经常用到的数据,如果不加redis缓存我们用户经常访问这些数据,就会经常访问mysql,但是MySQL数据是存在硬盘中的就会走IO,这时候我们可以将这些数据放到缓存中,如果缓存有就去缓存拿如果缓存没有就去MySQL,这样可以提高效率
缓存穿透
缓存和数据库中都没有的数据,那么这时候数据来访问redis缓存,当查数据时发现reids缓存没有去查询MySQL,导致每次查这个数据都去数据库查询,这样就像redis缓存被穿透了,MySQL大量过来请求导致宕机。
解决方案:
1 使用限流
2 创建一个空的,key:1 value:null 这样访问第一次发现数据库没有就保持这个key的value设置为null 但是这个设置的过期时间短一点,防止MySQL已经存在该数据了还是返回的null
3 使用布隆过滤器存储可能访问的key,当不存在这个key时就会把这个请求过滤
缓存击穿
当MySQL有这个数据但是redis没有这个数据,如果多个用户都来访问这个热点数据,导致大量用户并发读取缓存发现没有这个数据,同时去MySQL中读取,导致的数据库压力增大
解决方案:
1 热值热点数据永不过期 由定时任务定时更新缓存
2 添加互斥锁 同一时间只能有一个用户来访问,但是会导致变成单线程
缓存雪崩
大量的热点数据在同一时间过期,导致数据访问时发现缓存中key已经过期了,热点数据的访问很多都去MySQL中访问了,导致了MySQL请求量增大
解决方式:
1 过期时间打散,将热点数据的过期时间不弄在一起
2 缓存分布式 将数据均匀分布到不同的缓存数据库中
3 热点数据不过期 热点的数据不过期这样就防止了同一时间热点数据过期
4 添加互斥锁 该方式和缓存击穿一样,按 key 维度加锁,对于同一个 key,只允许一个线程去计算,其他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可。
spring-cache概述及核心配置
Spring Cache就是一个这个框架。它利用了AOP,实现了基于注解的缓存功能,并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了
使用步骤
添加依赖
<!--springboot的cache支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
添加配置
package com.itheima.restkeeper.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* @ClassName RedisCacheConfig.java
* @Description redis配置
*/
@Configuration
//开启caching的支持
@EnableCaching
public class RedisCacheConfig {
/**
* 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut)
* 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,
* 将数据添加到缓存之中或者从缓存中移除某个值
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//对key的序列化操作:String
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
//对value的序列化操作:json
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer =
new GenericJackson2JsonRedisSerializer();
//配置config,指定超时时间记得key val 序列化处理
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
//指定全局超时时间【60S】
.entryTtl(Duration.ofSeconds(60))
//配置key的序列化方式
.serializeKeysWith(RedisSerializationContext
.SerializationPair
.fromSerializer(redisSerializer))
//配置value的序列化方式
.serializeValuesWith(RedisSerializationContext
.SerializationPair
.fromSerializer(genericJackson2JsonRedisSerializer))
//关闭空值的存储
.disableCachingNullValues();
//使用建造者进行初始化
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.build();
}
}
配置类
#spring相关配置
spring:
main:
allow-bean-definition-overriding: true
redis:
redisson:
config: classpath:singleServerConfig.yaml
#redis配置信息
host: 192.168.112.77
port: 6379
password: pass
优雅使用spring-cache
选择Face的理由:
1、face是dubbo服务生
2、service的功能过于细腻,并且关联甚广
3、control层功能过于粗狂,不易缓存的维护
注解
对于缓存声明,spring的缓存提供了一组java注解:
-
@Cacheable:触发缓存写入,如果缓存中没有:查询数据库,存储缓存,返回结果。如果缓存中有:直接返回缓存结果
-
@CacheEvict:触发缓存清除。
-
@CachePut:更新缓存(不会影响到方法的运行)。
-
@Caching:重新组合要应用于方法的多个缓存操作
2.1、@Cacheable注解
==如果缓存中没有:查询数据库,存储缓存,返回结果,如果缓存中有:直接返回结果==
作用:可以用来进行缓存的写入,将结果存储在缓存中,以便于在后续调用的时候可以直接返回缓存中的值,而不必再执行实际的方法。 最简单的使用方式,注解名称=缓存名称,使用例子如下:
2.2、@CacheEvict注解
@CacheEvict:删除缓存的注解,这对删除旧的数据和无用的数据是非常有用的。这里还多了一个参数(allEntries),设置allEntries=true时,可以对整个条目进行批量删除
2.3、@CachePut注解
@CachePut:当需要更新缓存而不干扰方法的运行时 ,可以使用该注解。也就是说,始终执行该方法,并将结果放入缓存
2.4、@Caching注释
在使用缓存的时候,有可能会同时进行更新和删除,会出现同时使用多个注解的情况.而@Caching可以实现
//添加user缓存的同时,移除userPage的缓存 @Caching(put =@CachePut(value = "user",key ="#userVo.id"), evict = @CacheEvict(value = "userPage",allEntries = true))
==使用的规则:==
==1、项目启动时候需要增加哪些数据的热加载,以方便查询时候直接使用【@Cacheable】==
==2、增删改更新缓存【@CachePut,@CacheEvict】,查使用缓存【@Cacheable】==
==3、复杂场景中需要执行多条命令时候使用【@Caching】=