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 ...
随机推荐
- bzoj2056gift? 高精度?*
bzoj2056gift? 高精度? 题意: 给出abcdefghi,求2^a+2^b+2^c+2^d+2^e+2^f+2^g+2^h+i.a~h≤60,i≤2^63 题解: 发现只有极限数据才会爆u ...
- python3利用cryptography 进行加密和解密
我们的日常工作中,一定会遇到需要加密的数据,比如:密码.私密信息... ... 我们不仅要对他们进行加密,更需要对他们进行解密,因为毕竟我们的用户应该不会看得懂加密过后的字符串吧!!! 在python ...
- Active Directory - Creating users via PowerShell
Method1: Create a user by executing the following PowerShell Script. New-ADUser -name 'Michael Jorda ...
- OSCP Learning Notes - Buffer Overflows(5)
Generating Shellcode & Gaining Root 1.Generate the shellcode on Kali Linux. LHOST is the IP of K ...
- Python数据可视化基础讲解
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:爱数据学习社 首先,要知道我们用哪些库来画图? matplotlib ...
- 题解 洛谷 P5331 【[SNOI2019]通信】
考虑用费用流解决本题. 每个哨站看作一个点,并将其拆为两个点,建图方式为: \(S \longrightarrow x_i\) 容量为\(1\),费用为\(0\) \(x_i \longrightar ...
- python如何编写win程序
python可以编写win程序.win程序的格式是exe,下面我们就来看一下使用python编写exe程序的方法. 编写好python程序后py2exe模块即可将其打包为exe程序. 实际操作过程: ...
- Dom运用1
1.简单计算器 <!-- 第一个数--> <input type="text"> <!-- 符号复选框--> <select name=& ...
- 《Python编程初学者指南》高清PDF版|百度网盘免费下载|Python基础
<Python编程初学者指南>|百度网盘免费下载| 提取码:03b1 内容简介 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.Python可以用于很多的领域,从科学计 ...
- 《Python游戏编程快速上手》|百度网盘免费下载|Python基础编程
<Python游戏编程快速上手>|百度网盘免费下载| 提取码:luy6 Python是一种高级程序设计语言,因其简洁.易读及可扩展性日渐成为程序设计领域备受推崇的语言. 本书通过编写一个个 ...