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 ...
随机推荐
- 精通java并发-wait,notify和notifyAll的总结(含案例)
目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages wait,notify和notifyAll 总结 在调用wait方法时,线程必须要持有被调用对象的锁,当调用wait方 ...
- bzoj4582[Usaco2016 Open]Diamond Collector
bzoj4582[Usaco2016 Open]Diamond Collector 题意: n个钻石,每个都有一个大小,现在将其装进2个盒子里,每个盒子里的钻石最大的与最小的大小不能超过k,问最多能装 ...
- springboot --- 之SSM框架整合
1.pom依赖: 即:spring-boot的基本jar ---- 内置springmvc和spring Thymeleaf jar 热部署 jar ---方便二次加载 ctrl+f9再次编译 Myb ...
- Burp Suite Target Module - 目标模块
模块目的之一:获取网站分析 1.从Proxy - HTTP history界面选中需要加入Target Scope的Host 地址,右击,选中Add to Scope. 2.打开Target - Sc ...
- OSCP Learning Notes - WebApp Exploitation(4)
Local File Inclusion[LFI] Target Pentester Lab: Download from the following website: https://www.vul ...
- P5836 [USACO19DEC]Milk Visits S 从并查集到LCA(最近公共祖先) Tarjan算法 (初级)
为什么以它为例,因为这个最水,LCA唯一黄题. 首先做两道并查集的练习(估计已经忘光了).简单来说并查集就是认爸爸找爸爸的算法.先根据线索理认爸爸,然后查询阶段如果发现他们的爸爸相同,那就是联通一家的 ...
- 题解 CF1354D 【Multiset】
考试拿到题,一看,这不是权值线段树吗? 思路 使用线段树每个节点维护该区间内元素出现次数. 根据题目,对于加入.删除元素,我们可以单点修改(\(+1\).\(-1\)),对于输出,我们可 随便 遍历找 ...
- Email正则表达式验证
在做邮箱验证的时候,进行简单的整理: string emailStr = @"/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3 ...
- 小特跨境电商ERP桌面版 1.不止包括进销存 还能算毛利
经过近几年跨境电商ERP的开发,加上对跨境电商行业的业务积累,开发出一套适合中小跨境电商公司使用的ERP软件.欢迎选购.支持目前流行的各大电商平台,如速卖通.敦煌网.亚马逊.Shopee.Wish等. ...
- 【线性表基础】基于线性表的简单算法【Java版】
本文描述了基于线性表的简单算法及其代码[Java实现] 1-1 删除单链表中所有重复元素 // Example 1-1 删除单链表中所有重复元素 private static void removeR ...