一、使用场景

  我们在日常的开发中,经常会遇到查询数据列表的问题,有些数据是不经常变化的,如果想做一下优化,在提高查询的速度的同时减轻数据库的压力,那么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. ssm+PageHelper实现分页查询

    通过搭建ssm框架,然后通过mybatis的分页插件pagehelp进行分页查询.源码:https://gitee.com/smfx1314/pagehelper 看一下项目结构: 首先创建一个mav ...

  2. BZOJ1853 Scoi2010 幸运数字 【枚举+容斥】

    BZOJ1853 Scoi2010 幸运数字 Description 在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号 ...

  3. 项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦)

    知道了 csproj 文件中的一些常用 NuGet 属性,创建 NuGet 包时就可以充分发挥新 Sdk 自动生成 NuGet 包的优势,不需要 nuspec 文件啦.(毕竟 nuspec 文件没有 ...

  4. 《DSP using MATLAB》示例Example 7.13

    代码: M = 25; alpha = (M-1)/2; n = [0:1:M-1]; hd = (2/pi) * ( (sin( (pi/2)*(n-alpha) ).^2)./(n-alpha) ...

  5. drone 学习二 pipeline 说明

    1. 基本语法 pipeline: backend: image: golang commands: - go build - go test frontend: image: node comman ...

  6. bzoj1089严格n元树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1089 这是一种套路:记录“深度为 i ”的话,转移需要讨论许多情况:所以可以记录成“深度&l ...

  7. Apache + Tomcat + 连接器JK

    一 安装Apache 下载apache: https://www.apache.org/   ->  http://mirrors.cnnic.cn/apache/  -> http:// ...

  8. 5.Python使用最新爬虫工具requests-html

    1.安装,在命令行输入:pip install requests-html,安装成功后,在Pycharm引入即可. 2.代码如下所示: from requests_html import HTMLSe ...

  9. webdriver常用API

    本章涉及Selenium WebDriver的所有接口. Recommended Import Style 推荐的导入风格如下: from selenium import webdriver 然后,你 ...

  10. jQuery实现页面监听(某一个值发生改变时触发)

    使用jQuery实现页面中监听   页面中某一个值(如input输入框)发生改变时触发. <html> <head> <title>RunJS</title& ...