一、使用场景

  我们在日常的开发中,经常会遇到查询数据列表的问题,有些数据是不经常变化的,如果想做一下优化,在提高查询的速度的同时减轻数据库的压力,那么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. Servlet实现数字字母验证码图片(二)

    Servlet实现数字字母验证码图片(二): 生成验证码图片主要用到了一个BufferedImage类,如下:

  2. Windows 10 四月更新,文件夹名称也能区分大小写?

    Windows 向来是不区分文件和文件夹大小写的,但是从 NTFS 开始却又支持区分文件夹大小写.而 Linux/Mac OS 一向都是区分文件和文件夹大小写的. 本文将推荐 Windows 10 四 ...

  3. [Scala函数特性系列]——按名称传递参数

    通常情况下,函数的参数是传值参数:即参数的值在它被传递给函数之前被确定.但是,如果我们需要编写一个接收参数不希望马上计算,直到调用函数内的表达式才进行真正的计算的函数.对于这种情况,Scala提供按名 ...

  4. 如何向map和reduce脚本传递参数,加载文件和目录

    本文主要讲解三个问题:       1 使用Java编写MapReduce程序时,如何向map.reduce函数传递参数.       2 使用Streaming编写MapReduce程序(C/C++ ...

  5. Clairewd’s message(哈希模板+)

    个人心得:一开始就是知道用哈希,但是无从下手,很明显是对哈希不太了解和思维不太好. 先来看一下这一题涉及到的哈希吧和这题的思路吧,思路就是对所给的密文用原文和翻译后进行hash处理,那么必然存在后面那 ...

  6. 接口测试基础——第6篇unittest模块(三)

    今天是unittest最后一讲,我们解决一下如何只运行一次setUp和tearDown方法以及简单的数据驱动的知识. 1.只运行一次setUp和tearDown方法 很简单,只需要把setUp和tea ...

  7. smarty基本用法,循环,判断

    require './smarty/Smarty.class.php'; $sm = new Smarty; $sm->setTemplateDir("./dir");//设 ...

  8. 使用cookie纪录访问次数

    由于是简单的demo,我就没有链接数据库,退出重新登陆访问次数清零,只能靠下刷新来维持下访问次数 把用户名和次数初始化放进cookie Cookie uesrnameCookie = new Cook ...

  9. 初学HTML之HTML介绍

    众所周知现在的H5.大数据.云计算都是热门的.其实想学好一门语言重点是多看多想多写多练. 我在博客中会从基础开始讲解HTML4.0.中间加入HTML5的新标签 在这先给大家推荐几个开发工具: note ...

  10. Log4j配置概述

    一.Log4j 简介 Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局).这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出.综合 ...