从.Net到Java学习第七篇——SpringBoot Redis 缓存穿透
场景描述:我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,可能DB就挂掉了。
穿透:频繁查询一个不存在的数据,由于缓存不命中,每次都要查询持久层。从而失去缓存的意义。
常用解决办法:
①用一个bitmap和n个hash函数做布隆过滤器过滤没有缓存的键。
②持久层查询不到就缓存空结果,有效时间为数分钟。
我这里使用的是双重检测同步锁方式。
修改AreaService接口,添加如下两个接口方法,selectAllArea2方法是可能会导致缓存穿透的方法。
List<Area> selectAllArea();
List<Area> selectAllArea2();
修改接口的实现类AreaServiceImpl:
@Autowired
private RedisService redisService;
private JSONObject json = new JSONObject(); /**
* 从缓存中获取区域列表
*
* @return
*/
private List<Area> getAreaList() {
String result = redisService.get("redis_obj_area");
if (result == null || result.equals("")) {
return null;
} else {
return json.parseArray(result, Area.class);
}
} @Override
public List<Area> selectAllArea() {
List<Area> list = getAreaList();
if (list == null) {
synchronized (this) {
list = getAreaList(); //双重检测锁
if (list == null) {
list = areaMapper.selectAllArea();
redisService.set("redis_obj_area", json.toJSONString(list));
System.out.println("请求的数据库。。。。。。");
} else {
System.out.println("请求的缓存。。。。。。");
}
}
} else {
System.out.println("请求的缓存。。。。。。");
}
return list;
} @Override
public List<Area> selectAllArea2() {
List<Area> list = getAreaList();
if (list == null) {
list = areaMapper.selectAllArea();
redisService.set("redis_obj_area", json.toJSONString(list));
System.out.println("请求的数据库。。。。。。");
} else {
System.out.println("请求的缓存。。。。。。");
}
return list;
}
运行程序,在浏览器中输入地址http://localhost:8083/boot/getAll,第一次访问
2018-06-22 10:21:24.730 INFO 10436 --- [nio-8083-exec-1] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
请求的数据库。。。。。。
刷新浏览器地址,第二次访问
请求的缓存。。。。。。
再打开我们的redis可视化管理工具
在之前配置mysql数据库连接的时候,由于没有指定是否采用SSL,所以控制台会有一个警告信息,如下所示:
这个是因为使用的mysql版本比较高,要求开启SSL,所以控制台会有一个警告,当然,你也可以忽略,如果要去除这个警告,可以在之前的mysql连接配置后面添加:&useSSL=false
datasource:
url: jdbc:mysql://localhost:3306/demo?&useSSL=false
删除redis中的这个key值,我们通过使用一个并发测试工具来模拟缓存穿透的现象,这里使用到了jmeter这个并发测试工具。jmeter官网: https://jmeter.apache.org/。jmeter更多使用教程:https://www.yiibai.com/jmeter/
将jmter下载到本地,然后解压,双击jmeter.bat运行
(1)右键单击“测试计划”,新建测试组
(2)新建HTTP请求
(3)保存并运行测试,这是时候其实已经在开始运行了,我们可以通过“选项"——“Log Viewer",来查看运行日志。
此时再查看IDEA中的控制台运行情况如下:
我们看到有四次进行了数据库查询,而我们想要的其实是只进行一次数据库查询,其它的都是直接从缓存中进行查询。
重新删除redis中的key值redis_obj_area,我们再来测试一下采用了双重检测同步锁的方法selectAllArea2
修改jmeter中的请求路径
然后运行,我们再看下IDEA中控制台中的记录:
现在只有第一次是从数据库中读取了。
当然,如果我们不采用测试工具的话,我们也可以自己写一个单元测试,来进行并发测试。
单元测试类AreaServiceImplTest的代码:
@RunWith(SpringRunner.class)
@SpringBootTest
public class AreaServiceImplTest {
@Autowired
public AreaService areaService;
@Before
public void setUp() throws Exception { } @Test
public void selectAllArea() throws InterruptedException {
final CountDownLatch latch= new CountDownLatch(4);//使用java并发库concurrent
//启用10个线程
for(int i=1;i<=10;i++){
new Thread(new Runnable(){
public void run(){
try {
//Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
areaService.selectAllArea();
System.out.println(String.format("子线程%s执行!",Thread.currentThread().getName()));
latch.countDown();//让latch中的数值减一
}
}).start();
}
//主线程
latch.await();//阻塞当前线程直到latch中数值为零才执行
System.out.println("主线程执行!");
}
@Test
public void selectAllArea2() throws InterruptedException {
final CountDownLatch latch= new CountDownLatch(4);//使用java并发库concurrent
//启用10个线程
for(int i=1;i<=10;i++){
new Thread(new Runnable(){
public void run(){
try {
//Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
areaService.selectAllArea2();
System.out.println(String.format("子线程%s执行!",Thread.currentThread().getName()));
latch.countDown();//让latch中的数值减一
}
}).start();
}
//主线程
latch.await();//阻塞当前线程直到latch中数值为零才执行
System.out.println("主线程执行!");
}
@Test
public void selectAllArea3(){
Runnable runnable=new Runnable() {
@Override
public void run() {
areaService.selectAllArea2();
}
};
ExecutorService executorService=Executors.newFixedThreadPool(4);
for (int i=0;i<10;i++){
executorService.submit(runnable);
}
}
}
运行结果,和使用jmeter是差不多的。
从.Net到Java学习第七篇——SpringBoot Redis 缓存穿透的更多相关文章
- 从.Net到Java学习第十一篇——SpringBoot登录实现
从.Net到Java学习系列目录 通过前面10篇文章的学习,相信我们对SpringBoot已经有了一些了解,那么如何来验证我们的学习成果呢?当然是通过做项目来证明啦!所以从这一篇开始我将会对之前自己做 ...
- 从.Net到Java学习第八篇——SpringBoot实现session共享和国际化
从.Net到Java学习系列目录 SpringBoot Session共享 修改pom.xml添加依赖 <!--spring session--> <dependency> & ...
- Java学习第七篇:与运行环境交互
目录 一.与用户互动 1.main方法形参 2.使用Scanner类获取输入 3.使用BufferedReader类获取输入 二.常用类 1.System类和Runtime类 2.String, St ...
- 从.Net到Java学习第六篇——SpringBoot+mongodb&Thymeleaf&模型验证
SpringBoot系列目录 SpringBoot整合mongodb MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.如果你没用过Mong ...
- Java学习之反射篇
Java学习之反射篇 0x00 前言 今天简单来记录一下,反射与注解的一些东西,反射这个机制对于后面的java反序列化漏洞研究和代码审计也是比较重要. 0x01 反射机制概述 Java反射是Java非 ...
- Java学习之jackson篇
Java学习之jackson篇 0x00 前言 本篇内容比较简单,简单记录. 0x01 Json 概述 概述:JSON(JavaScript Object Notation, JS 对象简谱) 是一种 ...
- Java学习之注解篇
Java学习之注解篇 0x00 前言 续上篇文章,这篇文章就来写一下注解的相关内容. 0x01 注解概述 Java注解(Annotation)又称Java标注,是JDK5.0约会的一种注释机制. 和J ...
- 第04项目:淘淘商城(SpringMVC+Spring+Mybatis)【第七天】(redis缓存)
https://pan.baidu.com/s/1bptYGAb#list/path=%2F&parentPath=%2Fsharelink389619878-229862621083040 ...
- 从.Net到Java学习第四篇——spring boot+redis
从.Net到Java学习系列目录 “学习java已经十天,有时也怀念当初.net的经典,让这语言将你我相连,怀念你......”接上一篇,本篇使用到的框架redis.FastJSON. 环境准备 安装 ...
随机推荐
- HTML5调用手机摄像机、相册功能 <input>方法
最近用MUI框架做webapp项目,在有PLUS环境的基础上能直接调用手机底层的API来使用拍照或从相册选择上传功能! 在查资料的时候,想起了另一种用input调用摄像和相册功能的方法,之前没有深入了 ...
- Oracle SQL性能优化的40条军规
1. SQL语句执行步骤 语法分析> 语义分析> 视图转换 >表达式转换> 选择优化器 >选择连接方式 >选择连接顺序 >选择数据的搜索路径 >运行“执 ...
- [Swift]LeetCode551. 学生出勤纪录 I | Student Attendance Record I
You are given a string representing an attendance record for a student. The record only contains the ...
- [Swift]LeetCode762. 二进制表示中质数个计算置位 | Prime Number of Set Bits in Binary Representation
Given two integers L and R, find the count of numbers in the range [L, R] (inclusive) having a prime ...
- [Swift]LeetCode1027. 最长等差数列 | Longest Arithmetic Sequence
Given an array A of integers, return the length of the longest arithmetic subsequence in A. Recall t ...
- Docker系列教程01-Centos7安装新版Docker教程(10步)
最近一直忙于开发,没有时间好好总结一下docker的知识.其实现在docker的教程已经很多很多了,但是很多系统的教程都是基于Ubuntu系统,因为官方推荐使用Ubuntu系统啊,原因在于Ubuntu ...
- 【机器学习】--Kmeans从初识到应用
一.前述 Kmeans算法一般在数据分析前期使用,选取适当的k,将数据分类后,然后分类研究不同聚类下数据的特点. Kmeans算法是一种无监督的算法. 常用于分组,比如用户偏好. 二.概念及原理 Km ...
- ElasticSearch(1)---Mysql同步数据到ElSearch
ElasticSearch同步Mysql 先讲项目需求:对于资讯模块添加搜索功能 这个搜索功能我就是采用ElasticSearch实现的,功能刚实现完,所以写这篇博客做个记录,让自己在记录下整个步骤和 ...
- 纽约工作日志流水账 Day 1
周六早上8:00从青岛登机,历经17个小时,终于在当地时间周六下午2点半到达目的地纽约. 被媳妇吐槽旁边坐了美女妹子,其实是个美国妹子,旁边人家还有男朋友,全程只和我说了2句话,Exc ...
- C#版(击败97.76%的提交) - Leetcode 557. 反转字符串中的单词 III - 题解
版权声明: 本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. Leetcod ...