一、使用场景

  我们在日常的开发中,经常会遇到查询数据列表的问题,有些数据是不经常变化的,如果想做一下优化,在提高查询的速度的同时减轻数据库的压力,那么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缓存穿透问题解决方案的更多相关文章

  1. [Redis] - 高并发下Redis缓存穿透解决

    高并发情况下,可能都要访问数据库,因为同时访问的方法,这时需要加入同步锁,当其中一个缓存获取后,其它的就要通过缓存获取数据. 方法一: 在方法上加上同步锁 synchronized //加同步锁,解决 ...

  2. redis缓存穿透,缓存击穿,缓存雪崩原因+解决方案

    一.前言 在我们日常的开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,所以这样看起来并没有什么问题,可是一旦涉及大数据量的需求,比如一些商品抢购的情景,或者是 ...

  3. Redis缓存穿透和缓存雪崩以及解决方案

    Redis缓存穿透和缓存雪崩以及解决方案 Redis缓存穿透和缓存雪崩以及解决方案缓存穿透解决方案布隆过滤缓存空对象比较缓存雪崩解决方案保证缓存层服务高可用性依赖隔离组件为后端限流并降级数据预热缓存并 ...

  4. Redis缓存穿透、缓存雪崩、redis并发问题 并发竞争key的解决方案 (阿里)

    阿里的人问我 缓存雪崩(大量数据在同一时间过期了)了如何处理,缓存击穿了如何处理,回答的很烂,做了总结: 把redis作为缓存使用已经是司空见惯,但是使用redis后也可能会碰到一系列的问题,尤其是数 ...

  5. redis缓存穿透穿透解决方案-布隆过滤器

    redis缓存穿透穿透解决方案-布隆过滤器 我们先来看一段代码 cache_key = "id:1" cache_value = GetValueFromRedis(cache_k ...

  6. 预防Redis缓存穿透、缓存雪崩解决方案

    最近面试中遇到redis缓存穿透.缓存雪崩等问题,特意了解下. redis缓存穿透: 缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有.这样就导致用户查询的时候,在缓存中找不到,每次都要去 ...

  7. redis与mysql性能对比、redis缓存穿透、缓存雪崩

    写在开始 redis是一个基于内存hash结构的缓存型db.其优势在于速读写能力碾压mysql.由于其为基于内存的db所以存储数据量是受限的. redis性能 redis读写性能测试redis官网测试 ...

  8. Redis 缓存穿透

    Redis 缓存穿透 https://www.cnblogs.com/jiekzou/p/9212114.html 场景描述:我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容 ...

  9. redis缓存穿透,缓存击穿,缓存雪崩

    概念解释 redis 缓存穿透 key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源.比如用一个不存在的用户id获取用户信息,不论缓存还是数据库 ...

随机推荐

  1. CSS 文本垂直居中对齐

    文本垂直居中对齐是一个很常见的问题,这里总结一下. 一.容器高度固定,单行文本垂直居中对齐 height:20px; line-height:20px; overflow:hidden; 二.容器高度 ...

  2. BJOI 模拟赛 #3 题解

    T1 一个网格,每个点有权值,求有多少条路径权值乘积不小于 $n$ $R,C \leq 300, n \leq 10^6$ sol: 暴力 dp 是 $O(R \times C \times n)$ ...

  3. FreeMarker自定义TemplateDirectiveModel

    [参考:http://blog.csdn.net/fangzhangsc2006/article/details/8687371] 在采用FreeMarker做前台视图模板的情况下,我们可以通过< ...

  4. c++ 中 毫秒级时间获取

    #include <time.h> clock_t start,ends; start=clock(); Sleep(); ends=clock(); cout<<ends-s ...

  5. python模块--config

    一.创建文件 ##-----------------创建数据表-------------------------- import configparser config = configparser. ...

  6. js中对数字进行正则判断

    <script type="text/javascript"> function SubmitCk(num) { var reg = /^([a-zA-Z0-9]+[_ ...

  7. idea创建文件类型失败(xml之类的失效

    https://blog.csdn.net/sutongxuevip/article/details/72832754

  8. bzoj 2535 && bzoj 2109 [Noi2010]Plane 航空管制——贪心

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2535 https://www.lydsy.com/JudgeOnline/problem.p ...

  9. 树的遍历——pat1043

    http://pat.zju.edu.cn/contests/pat-a-practise/1043 给予N个数字组成二叉搜索树,判断这个数列是否由先序遍历得出或是镜像先序遍历得出,若是则输出相应的后 ...

  10. NetCore 下集成SignalR并进行分组处理

    Tips: 1.注意跟普通版Net.MVC的前端处理方式不一样,以前可以connection.start()后直接done里面再做逻辑处理,现在不行了 建议做法是在具体的业务Hub里重写OnConne ...