分布式系统关注点(18)——「缓存穿透」和「缓存雪崩」到底啥区别?

如果第二次看到我的文章,欢迎 订阅我的个人「原创」公众号(跨界架构师)哟~ 和我一起唠嗑~

本文长度为 2805字 ,建议阅读 8 分钟。

坚持原创,每一篇都是用心之作~

 

 

有句话说得好,欲要使其毁灭,先要使其疯狂。当你沉浸在缓存所带来的系统tps飙升的喜悦中时,使你系统毁灭的种子也已经埋在其中。

 

而且,你所承载的tps越高,它所带来的毁灭性更大。

 

在前两篇《 360°全方位解读「缓存」 》和《 先写DB还是「缓存」? 》中,我们已经对缓存有了一定的认识,并且知道了关于缓存相关的「一致性」问题的最佳实践。

 

这次,我们就来聊聊隐藏在缓存中的毁灭性种子是什么?

 

 

我们从前一篇文章《 先写DB还是「缓存」? 》中多次提到的「cache miss」说起。

 

 

缓存雪崩

在前一篇文章《 先写DB还是「缓存」? 》中,我们多次提到了「cache miss」这个词,利用「cache miss」来更好的保障DB和缓存之间的数据一致性。

 

然而,任何事物都是有两面性的,「cache miss」在提供便利的同时,也带来了一个潜在风险。

 

这个风险就是「缓存雪崩」。

 

 

在图中的第二步,大量的请求并发进入,这里的一次「cache miss」就有可能导致产生「缓存雪崩」。

 

不过,虽然「cache miss」会产生「缓存雪崩」 ,但「缓存雪崩」并不仅仅产生于「cache miss」。

 

雪崩一词源于「雪崩效应」,是指像「多米勒骨牌」这样的级联反应。前面没顶住,导致影响后面,如此蔓延。( 关于对应雪崩的方式参考之前的文章,文末放链接

 

所以 「缓存雪崩」的根本问题是:缓存由于某些原因未起到预期的缓冲效果,导致请求全部流转到数据库,造成数据库压力过重

 

因此,流量激增、高并发下的缓存过期、甚至缓存系统宕机都有可能产生「缓存雪崩」问题。

 

 

怎么解决这个问题呢?宕机可以通过做高可用来解决( 可以参考之前的文章,文末放链接 )。而在“流量激增”、“高并发下的缓存过期”这两种场景下,也有两种方式可以来解决。

 

 

加锁排队

通过加锁或者排队机制来限制读数据库写缓存的线程数量。比如,下面的伪代码就是对某个key只允许一个线程进入的效果。

 

key = "aaa";
var cacheValue = cache.read(key);
if (cacheValue != null) {
    return cacheValue;
}
else {
    lock(key) {
        cacheValue = cache.read(key);
        if (cacheValue != null) {
            return cacheValue;
        } 
        else {
            cacheValue = db.read(key);
            cache.set(key,cacheValue);
        }
    }
    return cacheValue;
}

 

这个比较好理解,就不废话了。

 

 

缓存时间增加随机值

这个主要针对的是「缓存定时过期」机制下的取巧方案。它的目的是 避免多个缓存key在同一时间失效,导致压力更加集中

 

比如,你有10个key,他们的过期时间都是30分钟的话,那么30分钟后这10个key的所有请求会同时流到db去。

 

而这里说的这种方式就是将这10个key的过期时间打乱,比如设置成25、26、27、...、34分的过期时间,这样压力就被分散了,每分钟只有一个key过期。

 

最简单粗暴的方式就是在设置「过期时间」的时候加一个随机数字。

 

cache.set(key,cacheValue,30+random())

 

总体来看,相比后者,前者的适用面更广,所以 Z哥建议你用「加锁排队」作为默认的通用方案不失为一个不错的选择

 

 

「缓存穿透」、「缓存雪崩」傻傻分不清楚?

如果你听说过「缓存穿透」的话,可能会问:「缓存雪崩」和「缓存穿透」一样吗?

 

从产生的效果上看是一样的,但是过程不同。

 

来举个例子。例子纯属虚构,别太在意合理性~

 

在一个方圆一万里的地区内,只有一个修手机的老师傅。他收了一个徒弟,希望徒弟能帮他分担掉一部分的工作压力。这里的老师傅可以看作是DB,徒弟看作是缓存。 

 

老师傅对徒弟说,如果遇到你不会做的事你来请教我。 

 

然后,一个客户过来说要修一下他的卫星电话,徒弟去请教老师傅,老师傅说他也不会,先拒绝了吧。

 

但是由于没告诉他后续遇到修卫星电话的人该怎么做,所以后续这个客户一直来问,徒弟每次都又去请教老师傅。最终,在修卫星电话这件事上,徒弟并没有帮老师傅缓解任何的压力,快被烦死了。

 

上面这个故事就好比「缓存穿透」。

 

 

而「缓存雪崩」则是,由于徒弟年轻力壮,精力充沛,1小时能修20个手机,老师傅只能修10个( 但是手艺好,更考究 )。

 

然后,有一天徒弟请假了,但恰巧这天来了2000个修手机的,老师傅修不过来就被累垮了。

 

 

所以, 「缓存穿透」和「缓存雪崩」最终产生的效果是一样的,就是因为大量请求流到DB后,把DB拖垮 正如前面故事中的老师傅 )。

 

两者最大的不同在于, 「缓存雪崩」问题只要数据从db中找到并放入缓存就能恢复正常 徒弟休假归来 ), 而「缓存穿透」指的是所需的数据在DB中一直不存在的情况 老师傅也不会修 )。并且,由于DB中数据不存在,所以自然每次从缓存中也找不到( 徒弟也不会修 )。

 

 

清楚了两者的区别之后,我们下面就来聊聊「缓存穿透」的常见应对方式。

 

 

缓存穿透

「缓存穿透」有时也叫做「缓存击穿」,产生的逻辑过程是这样,一直在虚线范围内流转。

 

 

在这种场景下,缓存的作用完全失效,每次请求都“穿透”到了DB中。

 

可能你会想,为什么会存在大量的这种db中数据不存在的情况呢?其实,任何依赖外部参数进行查询的地方都可能有这个问题的存在。比如,一个文本输入框,本来是让你输入用户名的,但是手误输入了密码,自然就找不到数据咯。更主要的问题是,会有恶意分子利用这种机制来对你的系统进行攻击,击穿缓存搞垮你的数据库,导致整个系统全面瘫痪。

 

 

同样也有两种方式来解决这个问题。

 

 

布隆过滤器(bloomfilter)

布隆过滤器就是由一个很长的二进制向量和一系列随机映射函数组成,将确定不存在的数据构建到过滤器中,用它来过滤请求。这里就放个图,具体就不展开了,后续我们再聊( 有兴趣的可以先到搜索引擎搜《Space time trade-offs in hash coding with allowable errors》找到bloom的原始论文 )。

 

实现代码其实并不很复杂,参考论文或者网上其他作者的一些实现就可以写出来。

 

不过, 布隆过滤器有一个最大的缺点,也是其为了高效利用内存而付出的代价,就是无法确保100%的准确率

 

所以,如果你的场景要求是100%准确的,就只能用下面这种方式了。

 

 

缓存空对象

其实就是哪怕从db中取出的数据是“空(null)”,也把它丢失到缓存中。

 

 

这样一来,虽然缓存中存在着一个value为空的数据,但是至少他能表示“数据库里也没有不用找了”。

 

其实这个思路和布隆过滤器有些类似,但是它对内存的消耗会大很多,毕竟布隆过滤器是利用的bit位来存储。不过这种方式的优势是前面提到的,不会出现误差,而布隆过滤器的错误率会随着「位数」的增加而减少,会不断趋近于0,但不会为0。

 

 

总结

好了,我们一起总结一下。

 

这次呢,Z哥主要和你聊了隐藏在缓存中的两颗具有“毁灭性”的种子,「缓存雪崩」和「缓存穿透」,以及应对这两颗种子的常用方式。

 

而且,顺便帮你区分清楚了「缓存雪崩」和「缓存穿透」的差异。

 

希望对你有所启发。

 

 

 


 

相关文章:

 


 

作者: Zachary

出处: https://www.cnblogs.com/Zachary-Fan/p/destroyseed.html

 

如果你喜欢这篇文章,可以点一下左下角的「  大拇指  」。

这样可以给我一点反馈。: )

谢谢你的举手之劳。

 

▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。  欢迎 订阅我的 原创 微信公众号(跨界架构师)  哟~ 和我一起唠唠嗑~

定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31544142/viewspace-2642465/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/31544142/viewspace-2642465/

【源码免费下载链接】:https://renmaiwang.cn/s/gbu02 grep命令是一个强大的文本处理工具,在Linux系统中被广泛使用。它能够有效地搜索文件中的特定模式,并提供灵活的匹配方式以满足不同需求。作为全名是Global Regular Expression Print的软件,grep采用基于posix标准的正则表达式系统,支持基本功能扩展特性。 在实际操作中,用户可以通过调整选项来控制搜索结果的表现形式。例如,“-c”选项可以仅显示符合条件的结果数量,“-i”则能实现不区分大小写的匹配效果。“-n”帮助展示包含目标模式的具体行号,“-s”则用于隐藏错误信息,而“-v”则与之相反,只呈现不符合条件的行。 正则表达式的核心在于元字符的应用。这些特殊符号赋予了grep强大的搜索能力,从简单的字符匹配到复杂的模式构建都能胜任。例如,“^”“$”分别标识文本的起始结束位置;方括号内部的字符可以进行范围匹配;转义操作允许用户解除默认含义的影响。 实践过程中,用户会发现部分正则表达式功能仅限于grep的基本版本,而更复杂的扩展需要借助egrep或pcre库的支持。此外,结合perl语言风格的正则表达式接口也能进一步提升搜索效率。 值得注意的是,在使用这些工具时,开发者需特别留意特殊字符的转义规则以及括号内的范围限定。通过深入理解灵活运用这些机制,用户可以充分发挥grep的强大功能,完成各种复杂的文本处理任务。 综上所述,熟练掌握grep命令及其背后的正则表达式知识将显著提升文本处理效率准确性。希望本文能为您提供有价值的参考指导。
【源码免费下载链接】:https://renmaiwang.cn/s/uc6y9 在现代生活中,路由器扮演着不可或缺的角色,整合了多项服务器功能以极大地方便了网络管理与维护。然而,这种集成也可能引入网络安全性稳定性的潜在风险。当路由器无法正确分配IP地址时,可能导致网络故障并影响局域网的正常运作。本文将深入探讨如何解决此类问题。遇到多数客户端无法获取IP地址或登录速度缓慢的情况,建议首先运行该系统的`ipconfig`命令检查配置设置。如果发现客户端无法从路由器获得IP地址,并排除了基本故障后,应关注路由器的状态。若交换机指示灯正常且无拥堵或死机现象,则可能与路由器的 DHCP(动态主机配置协议)服务出现故障有关。 DHCP 作为局域网中自动分配IP地址的关键组件,确保网络设备正确连接上因特网至关重要。当客户端首次接入网络时,会向 DHCP服务器发送一个 DHCP discover广播包以获取IP地址。在Windows系统中,默认情况下,若1秒内未收到响应,客户端将在几秒后尝试(共4次)。如果始终无法获得响应,将显示错误信息并提示用户等待5分钟后重新连接。当DHCP服务器无响应或未被客户端接受时,也会导致IP分配失败。深入分析 DHCP工作流程可发现潜在问题可能源于服务器未能做出适当回应或丢失了部分响应包。在确认网络硬件配置无误(如网线连通)后,问题很可能与 DHCP 服务器相关。大量客户端请求可能导致服务器资源耗尽,从而降低响应速度并引发网络拥塞现象。为提升整体服务质量,理想做法是增强服务器性能或增加服务器数量,通过专设不同功能的服务器来优化负载分配。对于预算有限的机构(如学校),购买额外设备可能不切实际,在这种情况下需要对现有架构进行优化以避免过载问题。潜在解决方案包括限制客户端连接数、调整 DHCP 租约时间或升级路由器硬件软件性能,同时建议定期维护服务器以确保其
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值