Java --- redis7之缓存预热+雪崩+穿透+击穿
目录
一、缓存预热
二、缓存雪崩
三、缓存穿透
3.1、解决方案
3.1.1、空对象缓存或者缺省值
3.1.2、Goolge布隆过滤器Guava解决缓存穿透
四、缓存击穿
4.1、危害
4.2、解决方案
4.3、模拟百亿补贴活动案例
一、缓存预热
场景:MySQL有N条新记录,redis没有
1、MySQL做数据新增 ,redis利用回写机制,让它逐步实现100条新增记录的同步,部署发布版本的时候,自己人提前做一次redis同步。
2、通过中间件或者程序自行完成。
3、使用白名单
二、缓存雪崩
场景:
1、redis主机挂掉,redis全盘崩溃,偏硬件运维。
2、redis中有大量key同时过期大面积失效,偏软件开发。
预防和解决:
1、redis中key设置为永不过期或者过期时间错开
2、redis缓存集群实现高可用:①、主从+哨兵。②、redis集群。③、开启redis持久化机制aof/rdb,尽快恢复缓存集群。
3、多缓存结合预防雪崩:ehcache本地缓存+redis缓存。
4、服务降级:Hystrix或者阿里sentinel限流和降级
5、用钱购买阿里云---》云数据库redis。
三、缓存穿透
查询一条或N条数据,redis中没有,mysql中也没有,但请求每次都会去查询数据库,导致后台数据库压力暴增,就是缓存穿透。
3.1、解决方案
3.1.1、空对象缓存或者缺省值
黑客或恶意攻击:
黑客会对系统进行攻击,拿一个不存在的id去查询数据,会产生大量的请求到数据库查询。可能会导致数据库压力大而宕掉。
key相同攻击系统:
第一次访问到MySQL,空对象缓存后第二次就返回defaultNull缺省值,避免MySQL被攻击,不用到数据库中去走一圈了。
key不同攻击系统:
由于存在空对象缓存和缓存回写,redis中的垃圾key会越写越多(设置key过期时间)。
3.1.2、Goolge布隆过滤器Guava解决缓存穿透
白名单过滤器:
1、误判问题,概率小可以接受,不能从布隆过滤器中删除
2、全部合法的key都需要放入Guava版布隆过滤器+redis里面,不然数据就是返回null
代码实现:
改pom
<!--google开源guava--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.0</version></dependency>
测试案例:
/*** 创建Guava布隆过滤器测试*/@Testpublic void testGuavaAndBloomFilter(){//创建Guava布隆过滤器BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 100);//判断指定的元素是否存在System.out.println(bloomFilter.mightContain(1));System.out.println(bloomFilter.mightContain(2));System.out.println("加入后");//将元素添加进布隆过滤器bloomFilter.put(1);bloomFilter.put(2);System.out.println(bloomFilter.mightContain(1));System.out.println(bloomFilter.mightContain(2));}
使用百万数据测试:
@Service
@Slf4j
public class GuavaBloomFilterServiceImpl implements GuavaBloomFilterService {public static final Integer _1W = 10000;//定义guava布隆过滤器,初始容量public static final Integer SIZE = 100 * _1W;//误判率,误判率越小误判个数越少public static double fpp = 0.03;//创建guava布隆过滤器private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), SIZE,fpp);@Overridepublic void guavaBloomFilter() {//在guava布隆过滤器加入一百万白名单数据for (int i = 0; i < SIZE; i++) {bloomFilter.put(i);}//取十万个不在合法范围的数据测试ArrayList<Integer> list = new ArrayList<>(10 * _1W);//测试for (int i = SIZE + 1; i < SIZE+(10 * _1W) ; i++) {//判断在guava布隆过滤器是否存在if (bloomFilter.mightContain(i)){log.info("被误判:{}",i);list.add(i);}}log.info("误判总数为:{}",list.size());}
}
@RestController
@Api(tags = "Google的guavaBloomFilter")
@Slf4j
public class GuavaBloomFilterController {@Autowiredprivate GuavaBloomFilterService guavaBloomFilterService;@RequestMapping(value = "/guavaBloomFilter",method = RequestMethod.GET)@ApiOperation("guava布隆过滤器插入100万样本数据和10万测试数据")public void guavaBloomFilter(){guavaBloomFilterService.guavaBloomFilter();}
}
结论 :100000 / 3033 = 0.03033
源码分析:
四、缓存击穿
大量的请求同时查询一个key时,而这个热点key正好失效,就会导致大量的请求都打到数据库上面去。
4.1、危害
会造成某一刻数据库请求量过大,压力剧增。
4.2、解决方案
热点key失效的原因:
1、时间到了自然被清除但还是被访问到
2、删除的key,刚好被访问。
方案1:
差异失效时间,对于访问频繁的热点key,不设置过期时间。
方案2:
互斥更新,采用双检加锁策略。
4.3、模拟百亿补贴活动案例
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "百亿补贴活动模拟")
public class Product {//商品idprivate Long id;//商品名private String name;//商品价格private Integer price;//商品描述private String detail;
}
@Service
@Slf4j
public class BYBTTaskServiceImpl implements BYBTTaskService {public static final String BYBT_KEY = "bybt";public static final String BYBT_KEY_A = "bybt:a";public static final String BYBT_KEY_B = "bybt:b";@Autowiredprivate RedisTemplate redisTemplate;/*** 模拟MySQL添加商品* @return*/public List<Product> getProductFromById(){List<Product> list = new ArrayList<>();for (int i = 1; i <= 20; i++) {Random random = new Random();int id = random.nextInt(1000);Product product = new Product((long) id, "product" + i, i, "鸽子蛋" + i);list.add(product);}return list;}
// @PostConstructpublic void initBYBT(){log.info("启动定时器模拟百亿补贴活动开始。。。");//使用线程模拟定时任务,后台任务定时将MySQL里的活动商品刷新到redis中new Thread(()->{while (true){//从MySQL中查询数据,写入redisList<Product> list = this.getProductFromById();//删除过期keyredisTemplate.delete(BYBT_KEY);//使用redis的list数据结果存储最新数据redisTemplate.opsForList().leftPushAll(BYBT_KEY,list);//暂停2分钟模拟,模拟百亿补贴参加活动商品try {TimeUnit.MINUTES.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"a1").start();}@PostConstructpublic void initBYBTAB(){log.info("启动AB定时器模拟百亿补贴活动开始。。。" + DateUtil.now());//使用线程模拟定时任务,后台任务定时将MySQL里的活动商品刷新到redis中new Thread(()->{while (true){//从MySQL中查询数据,写入redisList<Product> list = this.getProductFromById();//先更新缓存B,让缓存B过期时间超过缓存A,缓存A突然失效,还有缓存B,以防止缓存击穿redisTemplate.delete(BYBT_KEY_B);redisTemplate.opsForList().leftPushAll(BYBT_KEY_B,list);//设置过期时间redisTemplate.expire(BYBT_KEY_B,86410L,TimeUnit.SECONDS);//更新缓存AredisTemplate.delete(BYBT_KEY_A);redisTemplate.opsForList().leftPushAll(BYBT_KEY_A,list);//设置过期时间redisTemplate.expire(BYBT_KEY_A,86400L,TimeUnit.SECONDS);//暂停2分钟模拟,模拟百亿补贴参加活动商品try {TimeUnit.MINUTES.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"a1").start();}
}
@RestController
@Slf4j
@Api(tags = "百亿补贴活动模拟")
public class BYBTTaskController {public static final String BYBT_KEY = "bybt";public static final String BYBT_KEY_A = "bybt:a";public static final String BYBT_KEY_B = "bybt:b";@Autowiredprivate RedisTemplate redisTemplate;/*** 分页查询,查询redis* @return*/@RequestMapping(value = "/findPage",method = RequestMethod.GET)@ApiOperation("查询商品,每次1页每页5条显示")public List<Product> findPage(int page,int size){List<Product> list =null;long start = (page - 1) * size;long end = start + size - 1;try {//使用redis的list里的lrange查询分页list = redisTemplate.opsForList().range(BYBT_KEY,start,end);if (CollectionUtils.isEmpty(list)){//查询为空就去mysql中查询return null;}log.info("参加活动商品:{}",list);} catch (Exception e) {//出现异常,一般redis出现事故log.error("bybt异常:{}",e);}return list;}@RequestMapping(value = "/findPageAB",method = RequestMethod.GET)@ApiOperation("双缓存查询商品,每次1页每页5条显示")public List<Product> findPageAB(int page,int size){List<Product> list =null;long start = (page - 1) * size;long end = start + size - 1;try {//先去缓存A中查找list = redisTemplate.opsForList().range(BYBT_KEY_A,start,end);if (CollectionUtils.isEmpty(list)){log.info("缓存A已失效,暂时采用缓存B");//若缓存A没有就去缓存B中查找list = redisTemplate.opsForList().range(BYBT_KEY_B,start,end);if (CollectionUtils.isEmpty(list)){//TODO 去MySQL查找return null;}}log.info("参加活动商品:{}",list);} catch (Exception e) {//出现异常,一般redis出现事故log.error("bybt异常:{}",e);e.printStackTrace();}return list;}
}
Java --- redis7之缓存预热+雪崩+穿透+击穿相关推荐
- 【redis】缓存预热雪崩穿透击穿
[redis]缓存预热雪崩穿透击穿(上) 文章目录 [redis]缓存预热雪崩穿透击穿(上) 前言 一.面试题 二.缓存预热 三.缓存雪崩 发生原因 预防+解决 高可用: 多缓存结合: 人民币玩家 四 ...
- 缓存的雪崩,击穿,穿透
缓存的雪崩,击穿,穿透 前言 在将今天的内容之前,我们先来了解一下什么是缓存,缓存是用来干什么的,常用的缓存有哪些? 什么是缓存 我们要知道缓存其实就是一个临时的存储器,那么缓存里的数据就不是持久化的 ...
- redis缓存的雪崩、击穿、穿透,淘汰策略,持久化
1.redis缓存的雪崩.击穿.穿透,在实际中如何处理? 雪崩:缓存不存在,数据库存在,高并发,大量的key 原因:大量数据同时过期,Redis宕机 解决方案:给缓存数据的过期时间上加上小的随机数,避 ...
- 今天带你们走进缓存的雪崩、击穿、穿透基本概念
在互联网时代,大流量.海量数据.高并发是每个企业都渴望又害怕的名词,渴望是因为它们代表着提供的服务用户愿意买单.有价值;害怕是因为一旦用户全上来了,系统不能正常为用户提供服务,让用户失望,最终选择离开 ...
- Redis 5.0.8+常见面试题(单线程还是多线程、先更新缓存还是数据库、雪崩穿透击穿解决办法...)
Redis 6.0 保姆级教程(含微服务案例与完整面试题):https://www.yuque.com/yuxuandmbjz/redis Redis是单线程还是多线程 ?为什么这么设计 ? Redi ...
- 关于缓存雪崩\穿透\击穿等一些问题
前言 设计一个缓存系统,不得不要考虑的问题就是:缓存穿透.缓存击穿与失效时的雪崩效应. 缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到 ...
- 缓存 雪崩 穿透 击穿
缓存雪崩 原因:缓存雪崩是并发量过大带来的一系列的雪崩效应.大量的请求涌入缓存导致缓存无法处理挂掉,接着大量的请求会涌入数据库导致数据库挂掉. 分析:处理此问题的关键就是避免并发量大的时候缓存不要挂掉 ...
- 5redis-----------redis高级--GEO-查询附近的人、基数统计算法HLL 、布隆过滤器、缓存雪崩穿透击穿-------全栈式开发44
redis高级 一.GEO查询附近的人 二.基数统计算法-HyperLogLog 三.布隆过滤器 四.缓存雪崩&缓存穿透 (一)缓存雪崩 (二)缓存穿透 (三)缓存击穿 一.GEO查询附近的人 ...
- 如何解决Redis缓存雪崩、击穿与穿透
Redis最常用使用的场景就是作为业务系统的缓存,既然是作为缓存,那么就不免会碰到缓存常见的问题,即雪崩.击穿与穿透,什么是缓存雪崩.击穿与穿透以及如何解决这几个问题呢?今天我们一起来探讨一下! 一. ...
最新文章
- jpg在线合并jpg_JPG如何运作
- gcc a.c 究竟经历了什么
- 【扫盲】什么是FTP、FTPS 和 SFTP?
- PMCAFF | 从200块到300万,史上最全自媒体报价单
- 转载一个pythonA*实现
- 基于Python+Django+Mysql的图书管理系统
- 英魂之刃显示已将战斗服务器,英魂之刃战略版来袭,是挂名游戏还是搞事情?玩家:过于真实!...
- 【tf.keras.Model】构建模型小结(部分问题未解决)
- 服务器lsass状态代码c0000005,lsass.exe是什么进程?Win7系统提示Lsass.exe系统错误的原因及解决方法...
- 经典简约风格教师求职简历免费word模板
- 锻炼!!!!最佳时间!!!!希望大家都要记得锻炼身体!!!!
- 《数据结构》实验报告四:串的模式匹配(BF算法、KMP算法)
- 赵小楼《天道》《遥远的救世主》解读(1)怎么评价《遥远的救世主》《天幕红尘》这两本书和《天道》这部电视剧?
- 前端基础之CSS标签样式
- windows找不到文件请确定文件名是否正确怎么办?
- 专门画像素图的软件_有哪几种简便的做像素画的软件?
- linkcloud:KVM虚拟化渐趋成熟 可与XEN和Vmware比肩
- 机器学习导论:什么是机器学习?
- 漏洞复现 用友畅捷通T+任意文件上传漏洞(CNVD-2022-60632)
- 10个基于python的的BBS论坛的源码