springboot中redis的缓存穿透问题
什么是缓存穿透问题??
我们使用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的缓存穿透问题的更多相关文章
- springboot中redis取缓存类型转换异常
异常如下: [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested ...
- springboot中redis做缓存时的配置
import com.google.common.collect.ImmutableMap;import org.slf4j.Logger;import org.slf4j.LoggerFactory ...
- springBoot 中redis 注解缓存的使用
1,首先在启动类上加上 @EnableCaching 这个注解 在查询类的controller,或service ,dao 中方法上加 @Cacheable 更新或修改方法上加 @CachePut 注 ...
- redis的缓存穿透、击穿、雪崩以及实用解决方案
今天来聊聊redis的缓存穿透.击穿.雪崩以及解决方案,其中解决方案包括类似于布隆过滤器这种网上一搜一大片但是实际生产部署有一定复杂度的,也有基于spring注解通过一行代码就能解决的,其中各有优劣, ...
- SpringBoot中Redis的set、map、list、value、实体类等基本操作介绍
今天给大家介绍一下SpringBoot中Redis的set.map.list.value等基本操作的具体使用方法 上一节中给大家介绍了如何在SpringBoot中搭建Redis缓存数据库,这一节就针对 ...
- Spring Boot WebFlux-07——WebFlux 中 Redis 实现缓存
第07课:WebFlux 中 Redis 实现缓存 前言 首先,补充下上一篇的内容,RedisTemplate 实现操作 Redis,但操作是同步的,不是 Reactive 的.自然,支持 React ...
- springboot mybatis redis 二级缓存
前言 什么是mybatis二级缓存? 二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace. 即,在不同的sqlsession中,相同的namespace下,相同 ...
- SpringBoot集成Redis实现缓存处理(Spring AOP实现)
第一章 需求分析 计划在Team的开源项目里加入Redis实现缓存处理,因为业务功能已经实现了一部分,通过写Redis工具类,然后引用,改动量较大,而且不可以实现解耦合,所以想到了Spring框架的A ...
- redis的缓存穿透 缓存并发 缓存失效
我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题: 缓存穿透 缓存并发 缓存失效 一.缓存穿透 Paste_Image.png Paste_Image.png ...
随机推荐
- python也能玩视频剪辑!moviepy操作记录总结
前几篇文章咱们介绍了一下图片的处理方式,今天咱们说说视频的处理.python能够支持视频的处理么?当然是肯定的,人生苦读,我用python.万物皆可python. moviepy库安装 今天咱们需要使 ...
- JavaWeb基础(day15)( http + tomcat + servlet + 响应)
HTTP+Tomcat+Servlet+响应 HTTP HTTP 超文本传输协议(Hyper Text Transfer Protocol ),一种网络协议. 协议的组成和过程 HTTP协议由 ...
- 时间序列知识图谱-《利用Python进行数据分析》
所有内容整理自<利用Python进行数据分析>,使用MindMaster Pro 7.3制作,emmx格式,源文件已经上传Github,需要的同学转左上角自行下载或者右击保存图片. 其他章 ...
- SparkCore
一.概述 1,定义 RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象.代码中是一个抽象类,它代表一个不可变.可分区.里面的元素可 ...
- 修改ElementUI样式的几种方式
ElementUI是一款非常强大的前端UI组件库,它默认定义了很多美观的样式,但是我们在实际开发过程中不可避免地遇到需要修改ElementUI默认样式.下面总结了几种修改默认样式的方法. 1. 新建全 ...
- 部署一套完整的Kubernetes高可用集群(二进制,v1.18版)
一.前置知识点 1.1 生产环境可部署Kubernetes集群的两种方式 目前生产部署Kubernetes集群主要有两种方式: kubeadm Kubeadm是一个K8s部署工具,提供kubeadm ...
- 小程序开发全栈1.2/3/4组件、flex布局、样式
1.2 组件 1.2.1 text组件 编写文本信息,类似于HTTP中的span 1.2.2 view组件 容器,类似于HTTP中的div 1.2.3 image组件 图片显示组件 1.3 页面fle ...
- python mysql中in参数化说明
第一种:拼接字符串,可以解决问题,但是为了避免sql注入,不建议这样写 还是看看第二种:使用.format()函数,很多时候我都是使用这个函数来对sql参数化的 举个例子: select * from ...
- Java基础之常用知识点博客汇总
正则: 正则表达式 :https://www.cnblogs.com/lzq198754/p/5780340.html 正则表达式大全:https://blog.csdn.net/zpz2411232 ...
- Bug:No mapping for GET /onepill//swagger-ui.html
SpringBoot使用Swagger2本来可以使用的,后来出现的异常No mapping for GET /swagger-ui.html,这个异常其实不用怎么解释,说白了就是找不到了. 遇到这种情 ...