Redis缓存能够有效地加速应用的读写速度,就DB来说,Redis成绩已经很惊人了,且不说memcachedb和Tokyo Cabinet之流,就说原版的memcached,速度似乎也只能达到这个级别。今天主要讲讲在使用Redis时经常遇到的几个问题。缓存雪崩、缓存击穿、缓存穿透、缓存预热、缓存更新、缓存降级。

v缓存雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效。所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

缓存雪崩示意图:

缓存失效时的雪崩效应对底层系统的冲击非常致命,那么应对Redis缓存雪崩有哪些方案呢?

1.1 加锁或者队列

可以考虑用加锁或者队列的方式防止大量线程对数据库的一次性进行读写,避免缓存失效时对数据库造成的巨大冲击。

以上效果还可以考虑接入Redis锁实现,具体可以参考《SpringBoot进阶教程(二十七)整合Redis之分布式锁》 加锁或者队列都是一个非常浅显的办法。虽然能够在一定的程度上缓解了数据库的压力,但同时也极大的降低了系统的吞吐量。

1.2 协调Redis过期时间

分析用户行为,尽量让缓存失效的时间均匀分布,最次也得随机分布,尤其是一些访问大的接口

@Override
public UserDetails getUserInfoById(Integer uid){
String key = String.format("user_info_id:%d",uid);
UserDetails userDetails = (UserDetails)templateRedis.opsForValue().get(key);
if(userDetails != null){
return userDetails;
}else{
userDetails = userDetailsMapper.getUserDetailsByUid(uid);
Random random = new Random();
int time = 600;
// type: 1: 大V用户 2: 网红 3: 普通用户
if(userDetails != null){
if(userDetails.getType() == 1){
time = 3600 + random.nextInt(3600);
// 如果有其他逻辑
}else if(userDetails.getType() == 2){
time = 1200 + random.nextInt(1200);
// 如果有其他逻辑
}else{
// 如果有其他逻辑
} redisTemplate.opsForValue().set(key, userDetails, time, TimeUnit.SECONDS);
}
} return userDetails;
}

这里主要还是结合业务场景让缓存失效的时间均匀分布,比如上面这段代码中,大V用户和网红用户一般粉丝都是上百万,所以可以缓存长点的时间也是可以的。

1.3 二级缓存

做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

1.4 保证缓存层服务高可用性

保证缓存层服务高可用性。如果缓存层设计成高可用的,即使个别节点、个别机器、甚至是机房宕掉,依然可以提供服务。

关于这一块,可以看看前面写到的《SpringBoot进阶教程(三十)整合Redis之Sentinel哨兵模式》《详解Redis Cluster集群》

1.5 依赖隔离组件为后端限流并降级。

需要对重要的资源(例如Redis、MySQL、外部接口)都进行隔离,让每种资源都单独运行在自己的线程池中。即使个别资源出现了问题,对其他服务没有影响。但是线程池如何管理,比如如何关闭资源池、开启资源池、资源池阀值管理,这些做起来还是相当复杂的。

v缓存穿透

缓存穿透是指查询一个一定不存在的数据。由于缓存不命中,并且出于容错考虑,如果从数据库查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。

缓存穿透示意图:

如何解决缓存穿透?对应的几个参考方案:

2.1 布隆过滤器(BloomFilter)

采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。由于请求的参数是不合法的(每次都请求不存在的参数),于是我们可以使用布隆过滤器(BloomFilter)或者压缩filter提前拦截,不合法就不让这个请求到数据库层。

BloomFilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小,

2.2 将空对象记录在缓存中。

如果数据库返回信息为null,也可以将这个空对象设置到缓存里边去。下次再请求的时候,就可以从缓存里边获取了,将空对象设置一个较短的过期时间。如1.1中的代码示例所示。

v缓存击穿

缓存击穿指的是热点key在某个特殊的场景时间内恰好失效了,恰好有大量并发请求过来了,造成DB压力。

其实缓存击穿和缓存雪崩从概念上来讲差不多,只是缓存击穿是某些热点key,而雪崩指的是大规模的key。

如何解决缓存击穿,对应的几个参考方案:

3.1 与1.1中类似,通过加锁或者队列的方式防止大量请求透过redis到DB中。

3.2 对于一些热点key,过期时间可以无限调长

将热点key过期时间无限调长,然后通过job服务来管理这些热点key不会过期,保证热点key(尤其是像排行榜、首页热度等需要大量计算的热点key)的稳定性。需要注意的是,job服务本身也存在不稳定性,比如部署job的服务挂了之类的。这里可以看看之前的一篇文章。《详解Supervisor进程守护监控》

v缓存降级

缓存降级是指当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

参加过去年天猫双11的朋友应该很清楚的能感受到降级,当时被吐槽的最狠的降级应该就是加入购物车,在结算的时候无法更改收货地址。只能使用默认收货地址,这得成就多少"前男友前女友"啊。

在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

ps:强于"马爸爸",在双11的海量并发面前,也得降级,无可厚非。只是大家在做降级的时候,一定得考虑好取舍。这反而是降级最大的难度。

v缓存预热

上初中第一次做化学实验的时候,大家就知道试管加热前需要先预热。缓存预热也是一个比较常见的概念,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题。用户直接查询事先被预热的缓存数据。

缓存预热思路:

直接写个缓存刷新页面,上线时手工操作下;
数据量不大,可以在项目启动的时候自动进行加载;
定时刷新缓存;

对于一些计算量非常大的接口,缓存预热肯定是得有的。

v缓存更新

除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

定时任务去清理过期的缓存;
提供手动清理缓存的接口。
当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

上面的这几个方案各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的;第二种人工成本太高;第三种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂。具体使用场景还得结合业务来区分对待。

v博客总结

Redis的出现确实很大程度上解决了sql的压力,善用Redis的各种机制已经成为一个必不可少的技能之一。

v源码地址

https://github.com/toutouge/javademo/tree/master/hellospringboot

作  者:请叫我头头哥

出  处:http://www.cnblogs.com/toutou/

关于作者:专注于基础平台的项目开发。如有问题或建议,请多多赐教!

版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信

声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

Redis缓存雪崩、缓存穿透、缓存击穿、缓存降级、缓存预热、缓存更新的更多相关文章

  1. 8.了解什么是 redis 的雪崩、穿透和击穿?redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 redis 的穿透?

    作者:中华石杉 面试题 了解什么是 redis 的雪崩.穿透和击穿?redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 redis 的穿透? 面试官心理分析 其实这是问到缓存必问的,因为缓 ...

  2. redis缓存雪崩、穿透、击穿概念及解决办法

    缓存雪崩 对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机.缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据 ...

  3. Redis 缓存雪崩、穿透、击穿

    缓存雪崩 定义: 同一时间所有 key 大面积失效,比如网站首页的数据基本上都是同一批次去缓存的. 解决方法: ① 存的时候设定随机的失效时间. ② 服务做熔断处理(异常或着慢查询 Hystrix 限 ...

  4. 什么是 redis 的雪崩、穿透和击穿?

    缓存雪崩 对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机.缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据 ...

  5. Redis 雪崩、穿透和击穿

    https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-caching-avalanche-and ...

  6. Redis 雪崩、穿透、击穿、并发、缓存讲解以及解决方案

    1.缓存雪崩 数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机. 比如一个雪崩的简单过程 1.redis集群大面积故障 2.缓存 ...

  7. Redsi缓存问题(穿透,击穿,雪崩)以及解决办法(分布式锁)【高并发问题】

    Redsi常见问题 缓存在高平发和安全压力下的一些问题 缓存击穿 是某一个热点key在高并发访问的情况下,突然失效,导致大量的并发大金mysql数据库的情况 缓存穿透 是利用redis和mysql的机 ...

  8. 关于redis的几件小事(七)redis缓存雪崩与穿透

    1.缓存雪崩 (1)什么是缓存雪崩 缓存雪崩指的是在同一时刻,缓存大量失效,导致大量的请求直接到了数据库,数据库压力剧增,引起系统崩溃.可能出现的情况有: ①大量的key设置了相同的过期时间,导致在缓 ...

  9. 缓存雪崩、穿透如何解决,如何确保Redis只缓存热点数据?

    缓存雪崩如何解决? 缓存穿透如何解决? 如何确保Redis缓存的都是热点数据? 如何更新缓存数据? 如何处理请求倾斜? 实际业务场景下,如何选择缓存数据结构 缓存雪崩 缓存雪崩简单说就是所有请求都从缓 ...

  10. Redis缓存雪崩和穿透的解决方法

    转载自: https://blog.csdn.net/qq_35433716/article/details/86375506 如何解决缓存雪崩?如何解决缓存穿透?如何保证缓存与数据库双写时一致的问题 ...

随机推荐

  1. struts2上传图片超过大小给出错误提示

    struts2上传图片超过大小给出错误提示        今天碰到上传图片太大,上传不上去返回input视图的界面,回显的错误信息却是乱码,整了好久才整出来,在这里做个记录,方便自己以后查阅,也希望能 ...

  2. MSQL Webpage

    Mars Nov 19, 2014

  3. 在XP上安装VS2002

    在2002 年,随着 .NET 口号的提出与 Windows XP/Office XP 的公布,微软公布了 Visual Studio .NET(内部版本为 7.0). 使用VS2002+Object ...

  4. 话题讨论&征文--谈论大数据时我们在谈什么 获奖名单发布

    从社会发展趋势的角度,非常明显大数据会是眼下肉眼可及的视野范围里能看到的最大趋势之中的一个.从传统IT 业到互联网.互联网到移动互联网,从以智能手机和Pad 为主要终端载体的移动互联网到可穿戴设备的移 ...

  5. oracle指定访问某表或某视图

    在oracle中,想创建一个账号,然后只能只读地访问指定的表,怎么搞? 一.为特定的表创建视图 创建视图的时候还可以加上过滤条件,连访问哪些数据都可以指定. create or replace vie ...

  6. 【BZOJ4952】lydsy七月月赛 E 二分答案

    [BZOJ4952]lydsy七月月赛 E 题面 题解:傻题...二分答案即可,精度有坑. #include <cstdio> #include <cstring> #incl ...

  7. 为编译器的实现者提供一个精确的定义:ANSI C

    编译器的实现 常用C++编译器推荐_w3cschool https://www.w3cschool.cn/cpp/cpp-zxm72ps8.html 常用C++编译器推荐 由 Alma 创建, 最后一 ...

  8. JAVA工厂方法模式(Factory Method)

    1.普通工厂模式 普通工厂模式:就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 1-1.建立Sender接口 public interface Sender { public void ...

  9. ftp上传文件不能上传到指定的文件夹

    首先是,使用ftp创建连接,这一点没有错误,但是在切换目录创建文件夹的时候出现了问题. 指定创建的文件夹,总是创建失败,切换目录同样失败.最后查看文件夹的权限才知道,没有权限的问题: 然后给img文件 ...

  10. linux CentOS7.2安装ffmpeg-3.0.2

    ffmpeg是一款视频处理软件,在php5.5前以php_ffmpeg.dll扩展的方式存在,通过网上查资料显示,从php5.5以后不支持该扩展了.那么在linux系统下安装ffmpeg.方法如下: ...