缓存穿透、缓存并发、热点缓存之最佳招式

【Redis】数据击穿、穿透和雪崩 文章目录一、数据雪崩二、数据穿透三、数据击穿 一、数据雪崩 定时刷新清理缓存中的key时,Redis缓存中大面积key失效,从而导致大量请求直接访问了数据库,大面积的缓存失效,打崩了 DB。 举例: 大量key同时过期,大面积失效,请求直接打到数据库。你想想如果挂的是一个用户服务的库,那其他依赖他的库所有接口几乎都会报错。如果没做熔断等策略基本上就是瞬间挂一片的节奏。 解决: 对症下药,避免缓存中出现大量key同时失效的情况。 设置失效时间。批量往 Redis 存数据的时候,把每个 Key 的失效时间都加 阅读详情

作者:小程故事多

https://www.jianshu.com/p/d96906140199


一、前言

我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题:

  • 缓存穿透

  • 缓存并发

  • 缓存失效

缓存穿透

注:

上面三个图会有什么问题呢?

我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,可能DB就挂掉了。那这种问题有什么好办法解决呢?

要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

有一个比较巧妙的作法是,可以将这个不存在的key预先设定一个值。

比如,"key" , “&&”。

在返回这个&&值的时候,我们的应用就可以认为这是不存在的key,那我们的应用就可以决定是否继续等待继续访问,还是放弃掉这次操作。如果继续等待访问,过一个时间轮询点后,再次请求这个key,如果取到的值不再是&&,则可以认为这时候key有值了,从而避免了透传到数据库,从而把大量的类似请求挡在了缓存之中。

缓存并发

有时候如果网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时设置缓存的情况,如果并发确实很大,这也可能造成DB压力过大,还有缓存频繁更新的问题。

我现在的想法是对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询。

这种情况和刚才说的预先设定值问题有些类似,只不过利用锁的方式,会造成部分请求等待。

缓存失效

引起这个问题的主要原因还是高并发的时候,平时我们设定一个缓存的过期时间时,可能有一些会设置1分钟啊,5分钟这些,并发很高时可能会出在某一个时间同时生成了很多的缓存,并且过期时间都一样,这个时候就可能引发一当过期时间到后,这些缓存同时失效,请求全部转发到DB,DB可能会压力过重。

那如何解决这些问题呢?

其中的一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

我们讨论的第二个问题时针对同一个缓存,第三个问题时针对很多缓存。

总结来看:

  1. 缓存穿透:查询一个必然不存在的数据。比如文章表,查询一个不存在的id,每次都会访问DB,如果有人恶意破坏,很可能直接对DB造成影响。

  2. 缓存失效:如果缓存集中在一段时间内失效,DB的压力凸显。这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。

当发生大量的缓存穿透,例如对某个失效的缓存的大并发访问就造成了缓存雪崩。

问题汇总

问题1:

如何解决DB和缓存一致性问题?

答:当修改了数据库后,有没有及时修改缓存。这种问题,以前有过实践,修改数据库成功,而修改缓存失败的情况,最主要就是缓存服务器挂了。而因为网络问题引起的没有及时更新,可以通过重试机制来解决。而缓存服务器挂了,请求首先自然也就无法到达,从而直接访问到数据库。那么我们在修改数据库后,无法修改缓存,这时候可以将这条数据放到数据库中,同时启动一个异步任务定时去检测缓存服务器是否连接成功,一旦连接成功则从数据库中按顺序取出修改数据,依次进行缓存最新值的修改。

问题2:

问下缓存穿透那块!例如,一个用户查询文章,通过ID查询,按照之前说的,是将缓存的KEY预先设置一个值,,如果通过ID插过来,发现是预先设定的一个值,比如说是“&&”,那之后的继续等待访问是什么意思,这个ID什么时候会真正被附上用户所需要的值呢?

答:我刚说的主要是咱们常用的后面配置,前台获取的场景。前台无法获取相应的key,则等待,或者放弃。当在后台配置界面上配置了相关key和value之后,那么以前的key &&也自然会被替换掉。你说的那种情况,自然也应该会有一个进程会在某一个时刻,在缓存中设置这个ID,再有新的请求到达的时候,就会获取到最新的ID和value。

问题3:

其实用redis的话,那天看到一个不错的例子,双key,有一个当时生成的一个附属key来标识数据修改到期时间,然后快到的时候去重新加载数据,如果觉得key多可以把结束时间放到主key中,附属key起到锁的功能。

答:这种方案,之前我们实践过。这种方案会产生双份数据,而且需要同时控制附属key与key之间的关系,操作上有一定复杂度。

问题4:

多级缓存是什么概念呢?

答:多级缓存就像我今天之前给大家发的文章里面提到了,将ehcache与redis做二级缓存,就像我之前写的文章 http://www.jianshu.com/p/2cd6ad416a5a 提到过的。但同样会存在一致性问题,如果我们需要强一致性的话,缓存与数据库同步是会存在时间差的,所以我们在具体开发的过程中,一定要根据场景来具体分析,二级缓存更多的解决是,缓存穿透与程序的健壮性,当集中式缓存出现问题的时候,我们的应用能够继续运行。

说明:本文中提到的缓存可以理解为Redis。

二、缓存穿透与并发方案

上文中介绍了关于缓存穿透、并发的一些常用思路,但是没有明确一些思路的使用场景,下面继续深入探讨。相信不少朋友之前看过很多类似的文章,但是归根结底就是二个问题:

  • 如何解决穿透

  • 如何解决并发

当并发较高的时候,其实我是不建议使用缓存过期这个策略的,我更希望缓存一直存在,通过后台系统来更新缓存系统中的数据达到数据的一致性目的,有的朋友可能会质疑,如果缓存系统挂了怎么办,这样数据库更新了但是缓存没有更新,没有达到一致性的状态。

解决问题的思路是:如果缓存是因为网络问题没有更新成功数据,那么建议重试几次,如果依然没有更新成功则认为缓存系统出错不可用,这时候客户端会将数据的KEY插入到消息系统中,消息系统可以过滤相同的KEY,只需保证消息系统不存在相同的KEY,当缓存系统恢复可用的时候,依次从mq中取出KEY值然后从数据库中读取最新的数据更新缓存。注意:更新缓存之前,缓存中依然有旧数据,所以不会造成缓存穿透。

下图展示了整个思路的过程:

看完上面的方案以后,又会有不少朋友提出疑问,如果我是第一次使用缓存或者缓存中暂时没有我需要的数据,那又该如何处理呢?

解决问题的思路:在这种场景下,客户端从缓存中根据KEY读取数据,如果读到了数据则流程结束,如果没有读到数据(可能会有多个并发都没有读到数据),这时候使用缓存系统中的setNX方法设置一个值(这种方法类似加个锁),没有设置成功的请求则sleep一段时间,设置成功的请求读取数据库获取值,如果获取到则更新缓存,流程结束,之前sleep的请求这时候唤醒后直接再从缓存中读取数据,此时流程结束。

在看完这个流程后,我想这里面会有一个漏洞,如果数据库中没有我们需要的数据该怎么处理,如果不处理则请求会造成死循环,不断的在缓存和数据库中查询,这时候我们会沿用我之前文章中的如果没有读到数据则往缓存中插入一个NULL字符串的思路,这样其他请求直接就可以根据“NULL”进行处理,直到后台系统在数据库成功插入数据后同步更新清理NULL数据和更新缓存。

流程图如下所示:

总结:在实际工作中,我们往往将上面二个方案组合使用才能达到最佳效果,虽然第二种方案也会造成请求阻塞,但是只是在第一次使用或者缓存暂时没有数据的情况下才会产生,在生产中经过检验在TPS没有上万的情况下是不会造成问题的。

三、热点缓存解决方案
1、缓存使用背景:

我们拿用户中心的一个案例来说明:每个用户都会首先获取自己的用户信息,然后再进行其他相关的操作,有可能会有如下一些场景情况:

  • 会有大量相同用户重复访问该项目。

  • 会有同一用户频繁访问同一模块。

2、思路解析
  • 因为用户本身是不固定的而且用户数量也有几百万尤其上千万,我们不可能把所有的用户信息全部缓存起来,通过第一个场景情况可以看到一些规律,那就是有大量的相同用户重复访问,但是究竟是哪些用户重复访问我们也并不知道。

  • 如果有一个用户频繁刷新读取项目,那么对数据库本身也会造成较大压力,当然我们也会有相关的保护机制来确实恶意攻击,可以从前端控制,也可以有采黑名单等机制,这里不在赘述。如果用缓存的话,我们又该如何控制同一用户繁重读取用户信息呢。

请看下图:

我们会通过缓存系统做一个排序队列,比如1000个用户,系统会根据用户的访问时间更新用户信息的时间,越是最近访问的用户排名越排前,系统会定期过滤掉排名最后的200个用户,然后再从数据库中随机取出200个用户加入队列,这样请求每次到达的时候,会先从队列中获取用户信息,如果命中则根据userId,再从另一个缓存数据结构中读取用户信息,如果没有命中则说明该用户请求频率不高。

JAVA伪代码如下所示:

for (int i = 0; i < times; i++) {
     user = new ExternalUser();
     user.setId(i+"");
     user.setUpdateTime(new Date(System.currentTimeMillis()));
     CacheUtil.zadd(sortKey, user.getUpdateTime().getTime(), user.getId());
     CacheUtil.putAndThrowError(userKey+user.getId(), JSON.toJSONString(user));
 }
 Set<String> userSet = CacheUtil.zrange(sortKey, 0, -1);
 System.out.println("[sortedSet] - " + JSON.toJSONString(userSet) );
 if(userSet == null || userSet.size() == 0)
     return;
 Set<Tuple> userSetS = CacheUtil.zrangeWithScores(sortKey, 0, -1);
 StringBuffer sb = new StringBuffer();
 for(Tuple t:userSetS){
     sb.append("{member: ").append(t.getElement()).append(", score: ").append(t.getScore()).append("}, ");
 }
 System.out.println("[sortedcollect] - " + sb.toString().substring(0, sb.length() - 2));
 Set<String> members = new HashSet<String>();
 for(String uid:userSet){
     String key = userKey + uid;
     members.add(uid);
     ExternalUser user2 = CacheUtil.getObject(key, ExternalUser.class);
     System.out.println("[user] - " + JSON.toJSONString(user2) );
 }
 System.out.println("[user] - "  + System.currentTimeMillis());
 String[] keys = new String[members.size()];
 members.toArray(keys);
 Long rem = CacheUtil.zrem(sortKey, keys);
 System.out.println("[rem] - " + rem);
 userSet = CacheUtil.zrange(sortKey, 0, -1);
 System.out.println("[remove - sortedSet] - " + JSON.toJSONString(userSet));

推荐阅读从一个程序员的角度告诉你:“12306”有多牛逼?
开源博客项目eblog完整搭建教程!SpringBoot 2.0 集成Atomikos、Durid 实现多数据源、分布式事务SpringBoot 深度调优,JVM 调优(低调,深度好文)权限设计的一些想法和思考好文!必须点赞
nvlddmkm事件ID14报错排查全指南:从找不到描述到显卡驱动崩溃 系统日志中的“无法找到描述”往往让人一头雾水,但这类提示其实隐藏着驱动故障的关键线索。Windows事件查看器通过事件源和ID记录系统异常,其中显卡驱动相关的内核模式驱动组件一旦超时失败,就会触发TDR机制,产生13、14、153等不同事件ID。理解这些ID的含义,比纠结“找不到描述”本身更重要。对于频繁出现的驱动崩溃问题,可以从日志频率和发生场景判断方向,再通过安全模式下彻底清理驱动、关闭冲突软件、检查供电和温度等步骤逐层定位。无论是游戏满载黑屏,还是睡眠唤醒后闪退,掌握NVIDIA驱动日志的解读逻辑,都 阅读详情

相关推荐

新手必看:如何用PhaGCN2和vConTACT2搞定宏病毒组注释(附避坑指南)

本文为宏病毒组研究新手提供了使用PhaGCN2和vConTACT2进行病毒注释的完整实战指南。内容涵盖从Conda环境配置、软件安装、参数解析到结果解读的全流程,并重点分享了整合双工具进行交叉验证的策略与常见报错解决方案,旨在帮助研究者高效、准确地完成病毒分类注释工作。

j2k3l4的博客 797

Redis的数据穿透、数据击穿与数据雪崩

什么是Redis的数据穿透、数据击穿与数据雪崩

Weber's blog 1401

Homebrew 必备软件清单:我的 macOS 开发环境搭建全记录

本文详细介绍了如何使用Homebrew在macOS上搭建高效的开发环境,包括基础配置、核心开发工具链、高效编码工具集等30个必备工具。通过Homebrew这一macOS缺失的包管理器,开发者可以一键安装命令行工具和GUI应用,大幅提升工作效率。文章还提供了日常维护命令和疑难问题解决方案,适合从新手到资深开发者的所有用户。

weixin_28836507的博客 353

数据雪崩和数据穿透、击穿

目录 1、数据雪崩 2、数据穿透 3、数据击穿 1、数据雪崩 问题: 定时刷新清理缓存中的key时,Redis缓存中大面积key失效,从而导致大量请求直接访问了数据库,导致数据库崩盘。 举个栗子: 目前电商首页以及热点数据都会去做缓存,一般缓存都是定时任务去刷新,或者查不到之后去更新缓存的,定时任务刷新就有一个问题。 如果首页所有 Key 的失效时间都是 12 小时,中午 12 点刷新的,我零点有个大促活动大量用户涌入,假设每秒 6000 个请求,本来缓存可以抗住每秒 5000 个请求,

Longtermevolution的博客 6928

零基础手把手教你做FMEDA

本文通过分步骤手把手来教大家具体如何做FMEDA,希望对工作中要做FMEDA的童鞋有帮助。对于工作当中用不到FMEDA的童鞋,也可以作为科普知识,更好地了解一下FMEDA及硬件功能安全。

NewCarRen的博客 9926

advanced installer 打包Flutter的Windows程序安装包(基本打包、开机自启、定时自动检测升级、包含卸载程序)

advanced installer 打包Flutter的Windows程序安装包(基本打包、开机自启、定时自动检测升级、包含卸载程序)

qq_45634989的博客 2878

Redis入门讲解(五种数据类型和穿透、击穿、雪崩及四种模式)

Redis的安装与使用及4种模式一、什么是Redis Redis的作用是什么?1.安装二、五种类型的简单使用:String类型String 类型lit 类型hash 类型set 类型zset 类型三、redis的穿透、击穿、雪崩3.1穿透3.2击穿3.3雪崩四、Redis的持久化方式 RDB和AOF4.1RDB(默认持久化方式)4.2AOF五、单机模式、主从模式、哨兵模式、集群模式5.1单机模式5.2主从模式5.3哨兵模式5.4集群模式 撒啊实打实的 一、什么是Redis Redis的作用是什么? Re

li836779537的博客 1608

告别零碎资料!手把手教你搞定ASTER L1T数据的预处理全流程(附ENVI实操)

本文详细介绍了ASTER L1T数据的预处理全流程,包括数据获取、质量检查、ENVI实操步骤(辐射定标、大气校正、几何精校正)以及热红外波段深度处理。特别适合地信领域初学者和研究人员,帮助快速掌握ASTER L1T数据处理技巧,提升定量反演研究效率。

weixin_30385925的博客 354

雪崩、击穿、穿透

雪崩就是同一时间缓存同时失效导致大量数据同时访问数据库导致数据库崩溃。 解决方案:随机时效值、定时刷新,不设置失效值。 穿透就是值数据在缓存中没有,而直接访问数据库,黑客使用id=-1跳过缓存直接访问数据库。 解决方案:数据库无论查询出什么值都写入redis,拉黑IP,布隆过滤器。 击穿:双十一,某个key特别火爆,但是突然失效了,会导致大量访问打到数据库上。 解决方案:加锁数据库查询之后再返回redis、永不过期。 ...

HyzKey2U的博客 611

25 - GRACE Mascon数据缺失月份数据插值

本篇基于文章的相关内容和文章公开的奇异谱分析(SSA)插值算法程序包,对grace Mascon数据种缺失的月份进行 插值 ,总结了使用程序包的相关过程,可以将该程序应用到其他数据处理插值计算中。

weixin_43339605的博客 1842

基于Simulink建模仿真的滑模控制的磁悬浮轴承稳定控制仿真

磁悬浮轴承(Magnetic Bearing)利用电磁力来支撑和定位转子,无需机械接触,因此在高速旋转设备中具有广泛应用。滑模控制(Sliding Mode Control, SMC)是一种鲁棒性强的非线性控制方法,适用于磁悬浮轴承的精确控制。以下是基于Simulink的磁悬浮轴承滑模控制系统的详细设计与仿真步骤。通过上述步骤,设计并实现了基于 Simulink 和 Control System Toolbox 的磁悬浮轴承滑模控制系统。

MHD0815的博客 779

【转】缓存穿透、缓存并发、热点缓存之最佳招式

Redis缓存穿透、缓存雪崩、redis并发问题分析 地址:https://juejin.im/post/5b961172f265da0ab7198f4d (一)缓存和数据库间数据一致性问题 分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存。我们只能采取合适的策略来降低缓存和数据库间数据不一致...

路漫漫其修远兮 450

华为云计算——FusionCompute创建虚拟机

华为云计算——FusionCompute创建虚拟机

jks212454的博客 3675

转载和积累系列 - 缓存穿透、缓存并发、热点缓存之最佳招式

目录 一、缓存常见问题 缓存穿透 缓存并发 缓存失效 问题汇总 二、缓存穿透与并发方案 三、热点缓存解决方案 一、缓存常见问题 我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题: 缓存穿透 缓存并发 缓存失效 缓存穿透 注: 上面三个图会有什么问题呢? 我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回。这个时候如果我们查询的某一

自娱自乐的代码人 2万+

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

再谈缓存穿透、缓存并发、热点缓存之最佳招式

一、前言 在之前的一篇缓存穿透、缓存并发、缓存失效之思路变迁文章中介绍了关于缓存穿透、并发的一些常用思路,但是个人感觉文章中没有明确一些思路的使用场景,本文继续将继续深化与大家共同探讨,同时也非常感谢这段时间给我提宝贵建议的朋友们。说明:本文中提到的缓存可以理解为Redis。 二、缓存穿透与并发方案 相信不少朋友之前看过很多类似的文章,但是归根结底就是二个问题: 如何解决穿透 如何解决并发 当并

小程故事多的博客 7764

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

redis有值查询返回null_Redis缓存穿透、缓存并发、热点缓存之最佳招式

一、前言我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题:缓存穿透缓存并发缓存失效缓存穿透注:上面三个图会有什么问题呢?我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时...

weixin_30620121的博客 468

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

缓存穿透、缓存并发、热点缓存之最佳招式 -- 转载

一、前言 在之前的一篇缓存穿透、缓存并发、缓存失效之思路变迁文章中介绍了关于缓存穿透、并发的一些常用思路,但是个人感觉文章中没有明确一些思路的使用场景,本文继续将继续深化与大家共同探讨,同时也非常感谢这段时间给我提宝贵建议的朋友们。 说明:本文中提到的缓存可以理解为Redis。 二、缓存穿透与并发方案 相信不少朋友之前看过很多类似的文章,但是归根结底就是二个问题: 如...

weixin_30455023的博客 96

缓存穿透、缓存并发、缓存雪崩、缓存抖动、热点缓存、缓存双写一致性等问题

对于高并发的业务场景,常用的技术手段包括黑白名单、限流防刷、熔断降级、兜底、隔离、多级缓存(客户端、CDN、NGINX、内存缓存、分布式缓存)等等。这些手段相互结合,才能应对高并发场景下的各种细分场景。本文总结了缓存方案需要考虑的几个问题。 第一,关于缓存穿透,是指大量访问缓存和数据库中都不存在的数据。对此,可以考虑使用参考博客1和2中提到的两种方案:缓存空对象(对应key数量有限、key重复请求概率较高的场景)和布隆过滤器(对应key数量有限、key重复请求概率较高的场景和key各不相同、key重复请求

Saintyyu的博客 1132
上一篇: 10 分钟实现 Spring Boot 发生邮件功能
下一篇: 传统@ServerEndpoint方式开发WebSocket应用和SpringBoot构建WebSocket应用程序
Java思维导图
博客等级 码龄10年 306粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值