什么是缓存穿透问题??

我们使用redis是为了减少数据库的压力,让尽量多的请求去承压能力比较大的redis,而不是数据库。但是高并发条件下,可能会在redis还没有缓存的时候,大量的请求同时进入,导致一大批的请求直奔数据库,而不会经过redis。使用代码模拟缓存穿透问题如下:

首先是service里面的代码:

@Service
public class NewsService {
@Autowired
private NewsDAO newsDAO; //springboot自动初始化,不需要我们进行配置,直接注入到代码中使用
@Autowired
private RedisTemplate<Object,Object> redisTemplate; public /*synchronized*/ List<News> getLatestNews(int userId,int offset,int limit){ //设置序列化方式,防止乱码
redisTemplate.setKeySerializer(new StringRedisSerializer()); //第一步:查询缓存
News news= (News) redisTemplate.opsForValue().get("newsKey");
//判断是否存在缓存
if(null == news){//查询数据库
news = newsDAO.selectByUserIdAndOffset(userId,offset,limit).get(0);
//
redisTemplate.opsForValue().set("newsKey",news); System.out.println("进入数据库。。。。。。。。"); }else{
System.out.println("进入缓存。。。。。。。。。");
}
return newsDAO.selectByUserIdAndOffset(userId,offset,limit); }
}

然后是使用线程池在Controller里面对请求进行模拟:

@Controller
public class HomeController {
@Autowired
UserService userService; @Autowired
NewsService newsService; //遇到的坑,如果不加method,页面启动不起来。
@RequestMapping(value = "/home",method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String index(Model model){
//这边是可以读出数据来的 //线程池------缓存穿透问题的复现
ExecutorService executorService = Executors.newFixedThreadPool(8*2); for(int i = 0;i < 50000;i++){
executorService.submit(new Runnable() {
@Override
public void run() {
List<News> newsList = newsService.getLatestNews(0,0,10);
}
});
} List<News> newsList = newsService.getLatestNews(0,0,10);
News news=newsList.get(0);
return news.getImage();
}
}

结果如图:大量的请求进入数据库,那么如何解决这个问题?

方法一、在方法上加锁:

@Service
public class NewsService {
@Autowired
private NewsDAO newsDAO; //springboot自动初始化,不需要我们进行配置,直接注入到代码中使用
@Autowired
private RedisTemplate<Object,Object> redisTemplate; //第一种方式:方法加锁
public synchronized List<News> getLatestNews(int userId,int offset,int limit){ //设置序列化方式,防止乱码
redisTemplate.setKeySerializer(new StringRedisSerializer()); //第一步:查询缓存
News news= (News) redisTemplate.opsForValue().get("newsKey");
//判断是否存在缓存
if(null == news){
//查询数据库
news = newsDAO.selectByUserIdAndOffset(userId,offset,limit).get(0);
//
redisTemplate.opsForValue().set("newsKey",news); System.out.println("进入数据库。。。。。。。。"); }else{
System.out.println("进入缓存。。。。。。。。。");
} return newsDAO.selectByUserIdAndOffset(userId,offset,limit); }
}

直接在方法上加锁,保证每次只有一个请求可以进入。但是这个方法存在一个缺陷,每次只有一个请求可以进入,请求处理的速度变得相当的慢,不利于系统的实时性。

方法二、使用双重校验锁:

@Service
public class NewsService {
@Autowired
private NewsDAO newsDAO; //springboot自动初始化,不需要我们进行配置,直接注入到代码中使用
@Autowired
private RedisTemplate<Object,Object> redisTemplate; //第一种方式:方法加锁
public /*synchronized*/ List<News> getLatestNews(int userId,int offset,int limit){ //设置序列化方式,防止乱码
redisTemplate.setKeySerializer(new StringRedisSerializer()); //第一步:查询缓存
News news= (News) redisTemplate.opsForValue().get("newsKey");
//判断是否存在缓存
if(null == news){ //第二种方式:双重检测锁
synchronized (this){
//查询数据库
news = newsDAO.selectByUserIdAndOffset(userId,offset,limit).get(0);
//
redisTemplate.opsForValue().set("newsKey",news); System.out.println("进入数据库。。。。。。。。");
} }else{
System.out.println("进入缓存。。。。。。。。。");
} return newsDAO.selectByUserIdAndOffset(userId,offset,limit); }
}

这个方法比较好,虽然不能保证只有一个请求请求数据库,但是当第一批请求进来,第二批之后的所有请求全部会在缓存取数据。

springboot中redis的缓存穿透问题的更多相关文章

  1. springboot中redis取缓存类型转换异常

    异常如下: [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested ...

  2. springboot中redis做缓存时的配置

    import com.google.common.collect.ImmutableMap;import org.slf4j.Logger;import org.slf4j.LoggerFactory ...

  3. springBoot 中redis 注解缓存的使用

    1,首先在启动类上加上 @EnableCaching 这个注解 在查询类的controller,或service ,dao 中方法上加 @Cacheable 更新或修改方法上加 @CachePut 注 ...

  4. redis的缓存穿透、击穿、雪崩以及实用解决方案

    今天来聊聊redis的缓存穿透.击穿.雪崩以及解决方案,其中解决方案包括类似于布隆过滤器这种网上一搜一大片但是实际生产部署有一定复杂度的,也有基于spring注解通过一行代码就能解决的,其中各有优劣, ...

  5. SpringBoot中Redis的set、map、list、value、实体类等基本操作介绍

    今天给大家介绍一下SpringBoot中Redis的set.map.list.value等基本操作的具体使用方法 上一节中给大家介绍了如何在SpringBoot中搭建Redis缓存数据库,这一节就针对 ...

  6. Spring Boot WebFlux-07——WebFlux 中 Redis 实现缓存

    第07课:WebFlux 中 Redis 实现缓存 前言 首先,补充下上一篇的内容,RedisTemplate 实现操作 Redis,但操作是同步的,不是 Reactive 的.自然,支持 React ...

  7. springboot mybatis redis 二级缓存

    前言 什么是mybatis二级缓存? 二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace. 即,在不同的sqlsession中,相同的namespace下,相同 ...

  8. SpringBoot集成Redis实现缓存处理(Spring AOP实现)

    第一章 需求分析 计划在Team的开源项目里加入Redis实现缓存处理,因为业务功能已经实现了一部分,通过写Redis工具类,然后引用,改动量较大,而且不可以实现解耦合,所以想到了Spring框架的A ...

  9. redis的缓存穿透 缓存并发 缓存失效

    我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题: 缓存穿透 缓存并发 缓存失效 一.缓存穿透 Paste_Image.png Paste_Image.png ...

随机推荐

  1. log4j系统日志(转载)

    地址:http://www.codeceo.com/log4j-usage.html 日志是应用软件中不可缺少的部分,Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录.在a ...

  2. python 批量重命名文件名字

    import os print(os.path) img_name = os.listdir('./img') for index, temp_name in enumerate(img_name): ...

  3. Flutter 实现酷炫的3D效果

    老孟导读:此文讲解3个酷炫的3D动画效果. 下面是要实现的效果: Flutter 中3D效果是通过 Transform 组件实现的,没有变换效果的实现: class TransformDemo ext ...

  4. unittest学习笔记

    File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\errorhandler.py&quo ...

  5. Python对列表去重的各种方法

    一.循环去重   二.用 set() 去重 1.set()对list去重 2.list 是有序的,用 sort() 把顺序改回来  三.利用 dict 的属性来去重 1.用 dict 的 fromke ...

  6. 第一课:Centos下配置java环境变量的两种方式(jdk1.8)

    配置java环境(yum安装) 1.查出java1.8的全部版本 yum list java-1.8* 2.安装你需要的java1.8 版本(安装的名字根据查询出来的结果输入这里只是举例) yum i ...

  7. 一个文本框的andriod教程

    https://blog.csdn.net/androidmsky/article/details/49870823

  8. scp的使用以及cp的对比

    scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度.当你服务器 ...

  9. Android系统前台进程,可见进程,服务进程,后台进程,空进程的优先级排序

    1.前台进程 前台进程是Android中最重要的进程,在最后被销毁,是目前正在屏幕上显示的进程和一些系统进程,也就是和用户正在交互的进程. 2.可见进程 可见进程指部分程序界面能够被用户看见,却不在前 ...

  10. python学习笔记1 -- 面向对象编程高级编程1

    说起高级其实也就是些基础的东西,但是活用和熟用肯定会大幅度提升代码质量 首先要记录的是面向对象的灵活性,及如何去控制其灵活性,她允许你在实例中新增属性和方法,允许你给类新增属性和方法,也支持在定义类时 ...