Something before start
主要解决两种缓存场景
1、实时更新的缓存 即一般的查询时候,把记录写进缓存。
2、不需要实时的缓存 即不需要很准确的数据//比如用户数量什么的。对于这种情况,需要注意缓存雪崩的问题。这里是使用双重检测锁来解决这个问题的。
缓存雪崩:数据到期,大量请求涌入到数据库
依赖
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置文件
连接redis
有三种形式连接。另外注意配置redis的配置文件
# 关闭保护模式
protected-mode no
# 把这个注释掉,开启外地连接
#bind
单机
spring.redis.host=127.0.0.1
spring.redis.port=6379
#spring.redis.password=
哨兵模式集群
# 这个mymaster 可以直接在sentinel.conf里找到,可以更换成自己喜欢的hhhh
spring.redis.sentinel.master=mymaster
# 这里展示的是多种写法,多个哨兵
spring.redis.sentinel.nodes=locathost:26379,127.0.0.1:26380,locathost:26381
spring.redis.sentinel.password=
cluster集群
# 连接redis集群 //cluster模式
spring.redis.cluster.nodes=127.0.0.1:7006,127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003,127.0.0.1:7004,127.0.0.1:7005
#spring.redis.password=
指定springboot缓存形式
# 指定cache类型
spring.cache.type=redis
# 缓存区域名称 //随便取都行 同时可以多缓存空间
spring.cache.cache-names=realTimeCache
实体类序列化
很简单,只要实现java的序列化接口就行Serializable
。
序列化简单理解就是把一个对象转化成字符流
public class Employee implements Serializable {
private Integer id;
private String name;
private int age;
//省略其他乱七八糟的东西
注解方式
怎么确保缓存准确性
每次更新数据的时候就清理所有的缓存,在查询的时候将数据时写入缓存。因为redis本身性能很高,所以不用担心性能啥的。
启动类开启缓存
@EnableCaching
开启缓存
@EnableCaching
@EnableTransactionManagement
@SpringBootApplication
public class RedisdemoApplication {
更新数据 清理缓存
@CacheEvict
: 放在更新(增删改)数据的方法上
- value是前面配置的缓存区域名称。
- 当注解参数加上allEntries为true时,意思是说这个清除缓存是清除当前value值空间下的所有缓存数据。
//更新数据 则 更新清理缓存
@CacheEvict(value = "realTimeCache", allEntries = true)
@PostMapping("regist")
public void registHandle(Employee employee) {
employeeService.regist(employee);
}
查询数据 更新缓存
@Cacheable
:放在查询的方法上
//value:缓存空间 key:键
@Cacheable(value = "realTimeCache", key = "'emloyee_'+#id")
@GetMapping("find")
public Employee findHandle(int id) {
return employeeService.findEmployeeById(id);
}
API方式
什么是双重检测锁
第一次检测:进入方法,如果有值则返回,如果取出来为空则开启同步阻塞其他线程请求。
第二次检测:进入同步线程之后,再次检测是否有值,有则返回,无则将请求送到数据库。
为什么需要第二次检测:第一取不到值的线程进入同步的时候,可能会有其他请求跑进来。第一个线程更新完之后,其他的线程就不许要重新更新,所以需要第二次检测
实现代码
//使用双重检测锁 解决 热点缓存问题
@GetMapping("count")
public Integer countHandle() {
//获取redis操作对象 和count绑定 count即key
BoundValueOperations<Object, Object> ops = redisTemplate.boundValueOps("count");
//从缓存中获得数据
Object count = ops.get();
if (count==null){
//因为上锁之后阻塞了 会有新的请求到这里,所以需要上锁后重新检测保证后面来的不会击穿
//因为是单例 所以可以用this
synchronized (this){
count = ops.get();
if (count==null){
count = employeeService.findEmployeeCount();
ops.set(count,10, TimeUnit.SECONDS);//存入 存活10秒
}
}
}
return (Integer)count;
}
自动生成key
一样是使用codeConfig方式。
然后就可以把注解里的那个key删掉了
注意这里的para只适合一个参数的情况//其实写一个循环就完事了
暂时懒得写解释,感性理解一下就好/
@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {
//自动生成key结构:类名_方法名_参数
@Override
public KeyGenerator keyGenerator() {
return (target,method,params)->{
String className = target.getClass().getName();
String methodName = method.getName();
return className+"-"+methodName+"-"+params[0].toString();
};
}
}