Redis缓存穿透 解析
Posted 干桂旭
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis缓存穿透 解析相关的知识,希望对你有一定的参考价值。
先解析一下Redis中什么叫做 缓存穿透:
正常情况下我们查询同一条数据会先到Redis缓存中查取,如果没有再到数据库中查询,如果同一时间几千个用户查询同一条数据,在第一个线程到数据库中查询到了还未来得及更新到Redis里时剩下的线程也同时到数据库中查询,就无法体现Redis缓存的目的,这样被称为 Redis缓存穿透,大批量请求直接到数据库中查询,很容易导致数据库直接奔溃。
我们看一下会导致缓存穿透的代码:
# redisConfig是我封装的一个类
# 请求到了这个service 先去的redis查询,查询不到才会去数据库查询,但是没有添加锁 故导致了 缓存穿透
@Override
public Student findStudent(String key) {
long l1 = System.currentTimeMillis();
log.info("先去redis里面去取...");
Student student = (Student) redisConfig.get("student-"+key);
if (null == student){
log.info("redis中没有查到,到数据库中去取...");
student = studentDao.getStudent(Integer.valueOf(key));
redisConfig.set("student-"+student.getId(),student);
long l2 = System.currentTimeMillis();
log.info("l2-l1:{}",l2-l1);
}
long l3 = System.currentTimeMillis();
log.info("l3-l1:{}",l3-l1);
return student;
}
# 我用Jmeter测试了一个高并发场景 从截图中可以看到 多个线程 多次到数据库中查询了
我们再看一下加了锁之后的代码:
# 也就再方法上加了一个 synchronized
@Override
public synchronized Student findStudent(String key) {
long l1 = System.currentTimeMillis();
log.info("先去redis里面去取...");
Student student = (Student) redisConfig.get("student-"+key);
if (null == student){
log.info("redis中没有查到,到数据库中去取...");
student = studentDao.getStudent(Integer.valueOf(key));
redisConfig.set("student-"+student.getId(),student);
long l2 = System.currentTimeMillis();
log.info("l2-l1:{}",l2-l1);
}
long l3 = System.currentTimeMillis();
log.info("l3-l1:{}",l3-l1);
return student;
}
# 如截图所示 只有第一次去数据库中查询 剩下的线程都到Redis中查询 从实测的效果来看 缺点就是性能太差
我们再看一下双层检测锁的代码:
# 在方法内部加 synchronized 在 synchronized 内部再查一下redis
@Override
public Student findStudent(String key) {
long l1 = System.currentTimeMillis();
log.info("先去redis里面去取...");
Student student = redisConfig.get("student-"+key);
if (null == student){
//双层检测锁
synchronized (this){
student = redisConfig.get("student-" + key);
if (null == student){
log.info("redis中没有查到,到数据库中去取...");
student = studentDao.getStudent(Integer.valueOf(key));
redisConfig.set("student-"+student.getId(),student);
long l2 = System.currentTimeMillis();
log.info("l2-l1:{}",l2-l1);
}
}
}
long l3 = System.currentTimeMillis();
log.info("l3-l1:{}",l3-l1);
return student;
}
# 同样也解决了Redis缓存穿透 的情况 性能会比在方法上添加 synchronized 好很多
总结:
从如上代码中可以看到 如果要解决Redis数据穿透的问题 在方法内添加 synchronized 即可,建议使用双层检测锁 这样可以更大程度的保证性能;
以上是关于Redis缓存穿透 解析的主要内容,如果未能解决你的问题,请参考以下文章
Redis缓存穿透,缓存击穿,缓存雪崩解决方案以及封装Redis工具类
Redis缓存穿透,缓存击穿,缓存雪崩解决方案以及封装Redis工具类