高并发下redis缓存穿透问题解决方案
一、使用场景
我们在日常的开发中,经常会遇到查询数据列表的问题,有些数据是不经常变化的,如果想做一下优化,在提高查询的速度的同时减轻数据库的压力,那么redis缓存绝对是一个好的解决方案。
二、需求
假设有10000个请求,想达到第一次请求从数据库中获取,其他9999个请求从redis中获取这种效果。
三、代码实现
3.1、常规写法
public List<UsersDO> getAllUserWithNoPage2(){
try{
//序列化器,将key的值设置为字符串
RedisSerializer redisSerializer=new StringRedisSerializer();
redisTemplate.setKeySerializer(redisSerializer);
//查缓存
List<UsersDO> list=(List<UsersDO>)redisTemplate.opsForValue().get("allUsers");
if(null==list){
UsersQuery query=new UsersQuery();
list=usersDOMapper.selectByExample(query);
redisTemplate.opsForValue().set("allUsers", list);
System.out.println("从数据库中取数据");
}
else{
System.out.println("从缓存中取数据");
}
return list;
}
catch (Exception e) {
logger.error("UserService.getAllUserWithNoPage error",e);
}
return null;
}
常规的这种写法单线程没有问题,但是考虑到并发的存在,就会出现缓存渗透的问题,也就是不能保证其他9999个请求都是从redis中取。
3.2、常规写法压测
@GetMapping(value = "/test2")
public String test2(){
ExecutorService executorService= Executors.newFixedThreadPool(20); for(int i=1 ; i<=10000;i++){ executorService.submit(new Runnable() {
@Override
public void run() {
userService.getAllUserWithNoPage2();
}
});
} return "test over";
}
3.3、常规写法压测结果

3.4、常规写法的改进,使用双重检测锁
public List<UsersDO> getAllUserWithNoPage(){
try{
//序列化器,将key的值设置为字符串
RedisSerializer redisSerializer=new StringRedisSerializer();
redisTemplate.setKeySerializer(redisSerializer);
//查缓存
List<UsersDO> list=(List<UsersDO>)redisTemplate.opsForValue().get("allUsers");
if(null==list){
//双重检测 锁
synchronized (this) {
List<UsersDO> list1 = (List<UsersDO>) redisTemplate.opsForValue().get("allUsers");
if (null == list1) {
UsersQuery query=new UsersQuery();
list=usersDOMapper.selectByExample(query);
redisTemplate.opsForValue().set("allUsers", list);
System.out.println("从数据库中取数据");
}
else{
System.out.println("从缓存中取数据");
}
}
}
else{
System.out.println("从缓存中取数据");
}
return list;
}
catch (Exception e) {
logger.error("UserService.getAllUserWithNoPage error",e);
}
return null;
}
3.5、双重检测锁压测
@GetMapping(value = "/test")
public String test(){
ExecutorService executorService= Executors.newFixedThreadPool(20); for(int i=1 ; i<=10000;i++){ executorService.submit(new Runnable() {
@Override
public void run() {
userService.getAllUserWithNoPage();
}
});
} return "test over";
}
3.6、双重检测锁压测结果

压测结果符合要求。
完整代码已上传Github :传送门
高并发下redis缓存穿透问题解决方案的更多相关文章
- [Redis] - 高并发下Redis缓存穿透解决
高并发情况下,可能都要访问数据库,因为同时访问的方法,这时需要加入同步锁,当其中一个缓存获取后,其它的就要通过缓存获取数据. 方法一: 在方法上加上同步锁 synchronized //加同步锁,解决 ...
- redis缓存穿透,缓存击穿,缓存雪崩原因+解决方案
一.前言 在我们日常的开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,所以这样看起来并没有什么问题,可是一旦涉及大数据量的需求,比如一些商品抢购的情景,或者是 ...
- Redis缓存穿透和缓存雪崩以及解决方案
Redis缓存穿透和缓存雪崩以及解决方案 Redis缓存穿透和缓存雪崩以及解决方案缓存穿透解决方案布隆过滤缓存空对象比较缓存雪崩解决方案保证缓存层服务高可用性依赖隔离组件为后端限流并降级数据预热缓存并 ...
- Redis缓存穿透、缓存雪崩、redis并发问题 并发竞争key的解决方案 (阿里)
阿里的人问我 缓存雪崩(大量数据在同一时间过期了)了如何处理,缓存击穿了如何处理,回答的很烂,做了总结: 把redis作为缓存使用已经是司空见惯,但是使用redis后也可能会碰到一系列的问题,尤其是数 ...
- redis缓存穿透穿透解决方案-布隆过滤器
redis缓存穿透穿透解决方案-布隆过滤器 我们先来看一段代码 cache_key = "id:1" cache_value = GetValueFromRedis(cache_k ...
- 预防Redis缓存穿透、缓存雪崩解决方案
最近面试中遇到redis缓存穿透.缓存雪崩等问题,特意了解下. redis缓存穿透: 缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有.这样就导致用户查询的时候,在缓存中找不到,每次都要去 ...
- redis与mysql性能对比、redis缓存穿透、缓存雪崩
写在开始 redis是一个基于内存hash结构的缓存型db.其优势在于速读写能力碾压mysql.由于其为基于内存的db所以存储数据量是受限的. redis性能 redis读写性能测试redis官网测试 ...
- Redis 缓存穿透
Redis 缓存穿透 https://www.cnblogs.com/jiekzou/p/9212114.html 场景描述:我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容 ...
- redis缓存穿透,缓存击穿,缓存雪崩
概念解释 redis 缓存穿透 key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源.比如用一个不存在的用户id获取用户信息,不论缓存还是数据库 ...
随机推荐
- C:源文件编译过程
可以大致概括为3个阶段: 源文件 → 汇编代码(文本) 汇编代码 → 机器语言(二进制) 各个目标文件的处理 详细过程: 预编译处理 Pre-processing(*.c/ *.cpp → *.i) ...
- 每天一个linux命令:【转载】rm命令
今天学习一下linux中删除文件和目录的命令: rm命令.rm是常用的命令,该命令的功能为删除一个目录中的一个或多个文件或目录,它也可以将某个目录及其下的所有文件及子目录均删除.对于链接文件,只是删除 ...
- python正则的使用
python的正则是通过re模块的支持 匹配的3个函数 match :只从字符串的开始与正则表达式匹配,匹配成功返回matchobject,否则返回none: re.match(pattern, st ...
- c++ template不能有cpp
c++的template只能把生命和定义都放在.h文件里,不然会出错
- WebBrower使用 Http 代理访问网页
public struct Struct_INTERNET_PROXY_INFO { public int dwAccessType; public IntPtr proxy; public IntP ...
- nginx grpc 试用
1. 编译 wget https://nginx.org/download/nginx-1.13.10.tar.gz tar xvf nginx-1.13.10.tar.gz cd nginx-1.1 ...
- java之集合概述
集合也称容器:从大的类别分成两类:Collection和Map,也即:单列和双列列表. java编程思想中一张图说明该体系的整体结构:其中黑色着重的类是经常使用的类. 1 Collection Col ...
- oracle之 调整 I/O 相关的等待
I/O相关竞争等待简介 当Oracle数据库出现I/O相关的竞争等待的时候,一般来说都会引起Oracle数据库的性能低下,发现数据库存在I/O相关的竞争等待一般可以通过以下的三种方法来查看Oracle ...
- win7 QT +opencv环境搭建
1.Win7 Qt4.8.5+QtCreator2.8.0+mingw环境参考前博文先搭建 2.下载Cmake2.8.11.2版本,安装.运行 [项目]那编译器选择:MinGW4.4.另外,重新编译O ...
- struts2学习(5)拦截器简介以及例子执行过程
一.拦截器简介: 二.Struts2预定义拦截器&拦截器栈 在执行action之前和之后,拦截器进行了操作: 比如struts-default.xml中就有很多预定义的拦截器: 拦截器栈: ...