什么是缓存穿透问题??

我们使用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. 适用于IE8浏览器的bootsarp下拉菜单(支持多选,全选)

    html部分代码,引用及整体项目Github项目地址:https://github.com/CNbozi/combobox 1 <!DOCTYPE html> <html lang= ...

  2. DEX文件解析--5、dex方法原型解析

    一.前言    前几篇文章链接:     DEX文件解析---1.dex文件头解析     DEX文件解析---2.Dex文件checksum(校验和)解析     DEX文件解析--3.dex文件字 ...

  3. 【面试题资源共享】一文总结最高频软件测试|sq|语句|思维发散|计算机基础|Linux|测试用例|接口测试|等技术面试题

    思维发散 1.一个球, -把尺子长度是球直径的2/3,怎样测出半径?2.四枚硬币,花面朝上,每次翻转三个,几次可以将四枚硬币变为字面朝上?3. U2合唱团在1 7分钟内赶到演唱会现场问题?4.小明一家 ...

  4. OSCP Learning Notes - Privilege Escalation

    Privilege Escalation Download the Basic-pentesting vitualmation from the following website: https:// ...

  5. day10 python之函数的参数

    函数的基本属性 1.1 函数的含义 # 1.功能 :包裹代码,实现功能,达到目的 # 2.特点 :反复调用,提高开发效率,便于代码维护 1.1.2 函数的基本格式 # 函数名 :变量命名规则 # 函数 ...

  6. 【Python学习笔记六】获取百度搜索结果以及百度返回“百度安全验证”问题解决

    1.获取百度搜索结果页面主要是修改百度搜索url中的参数实现,例如查询的关键字为wd: 举例:https://www.baidu.com/s?wd=python",这样就可以查询到‘pyth ...

  7. vue配置 less 全局变量

    在使用Vue开发的过程中,通常会用到一些样式的全局变量,如果在每个组件中引入就太繁琐了,维护性也不好,因此全局引入是个不错的想法.下面以less为例,记录一下全局引入less变量的步骤: 1.首先安装 ...

  8. 脸书(Facebook)如何绑定谷歌二次验证码/谷歌身份验证/双重认证?

    1.打开Facebook,找到双重验证界面   打开Facebook,点击“设置”-“安全与登陆”-“使用双重验证”-“身份验证应用”-“在其他设备上设置应用”-“输入验证码” *****想使用Fac ...

  9. centos7 安装 isign

    centos应该自带python和openssl,这两个就不用装了, 先安装zip和git yum install -y unzip zip yum install git 然后克隆代码: https ...

  10. EOJ Monthly 2019.11 A(进制转换)

    "欢迎您乘坐东方航空公司航班 MU5692 由银川前往上海......" "我们的飞机很快就要起飞了,请收起小桌板,摘下耳机......" 收起了小桌板,摘下了 ...