高并发下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获取用户信息,不论缓存还是数据库 ... 
随机推荐
- SEO SEM
			SEO:搜索引擎优化SEM:搜索引擎营销 SEO排名机制:搜索引擎蜘蛛 权重 算法 排名规则 搜索引擎提交入口: 1.百度搜索网站登入口 2.Google网站登入口 3.360搜索引擎登入入口 4.搜 ... 
- HttpMessageConvert
			1. 我们先来看看框架会自动注册哪些httpmessageconvert? 在哪个地方开始注册的? 在对mvc:annotation-driven解析的AnnotationDrivenBeanDefi ... 
- ThinkPHP5 为什么取消了单字母函数?
			ThinkPHP5 为什么取消了单字母函数? 更容易理解. 理加规范. 个人喜好. 比如 TPShop 也是用 ThinkPHP5 又加回单字母函数. [话唠]教练,我想做菜-长沙 2018/10/8 ... 
- python中高阶函数学习笔记
			什么是高阶函数 变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数 def fun(x, y, f): print f(x), f(y) fun ... 
- WebApi和Andriod对接上传和下载文件
			我在实现webapi和Andriod客户端上传下载文件的时候默认的是以流的形式返回的,下面我就贴出最近在研究的对接文件的上传和下载代码以供各位大侠们参考: 上传文件接口: [HttpPost] pub ... 
- 启动Oracle数据库时报错ORA-00119 & ORA-00132
			今天启动Oracle数据库时报错ORA-00119 & ORA-00132,找到解决方法做个记录,方便日后查看. 若是ORACLE不提示错误的话,可以自己查看ORACLE的日志文件. Orac ... 
- shell常用测试命令
			预定义变量: 预定义变量是由Bash程序预先定义好的一类特殊变量,用户只能使用预定义变量,而不能创建新的预定义变量,也不能直接为预定义变量赋值.预定义比变量使用"$"符合和另一个符 ... 
- scanf在竞赛中的技巧总结ing
			前言 当输入流是一个字符串,我们需要在其中提取我们所需要的数值时,我们可以在读入阶段就完成数据的筛选工作. 使用方法 scanf("%ns", str); 表示读取长度为n的字符串 ... 
- HTML5的LocalStorage和sessionStorage的使用
			本文转载自:http://www.cnblogs.com/qiutianlidehanxing-blog/p/5953746.html html5中的Web Storage包括了两种存储方式:sess ... 
- pagination实现分页功能
			pagination.js: /** * pagination分页插件 */ ;(function($,window,document,undefined){ //配置参数 var defaults ... 
