当前位置: 首页 > 科技观察

缓存确实很香,但也很伤人!

时间:2023-03-21 20:39:13 科技观察

缓存的使用是一个逐渐演进的过程。问问自己,使用缓存最直接的原因是什么?没有它,它只是快!图片来自Pexels。回头看我第一次使用缓存的场景,一些不变的配置信息保存在数据库中,在服务启动的时候,直接加载到本地的公共模块中,方便其他功能模块共享使用。这是最基本最简单的本地缓存应用。服务与缓存所谓服务,简单来说就是一层应用+一层数据,应用从数据层获取数据,然后处理输出。数据层,通常我们指的是持久化介质上的持久化存储。它有多种形式,可以是文件,也可以是数据库。数据存储在持久性介质上,而应用程序在内存中运行。内存和持久性媒体是两种不同的媒体,速度有一个数量级的差异。因此,应用程序和数据之间存在“矛盾”。有了这种“自相矛盾”的介绍,就急需缓存了。我们所说的缓存必须是存储在内存中,这样才能更贴近应用程序,更快的给应用程序需要的数据,从而获得更快的服务响应。当然,并不是说缓存完全隔离了持久层数据。缓存,一个与它相提并论的词,叫做命中率。当我们查询的数据存在于缓存中时,我们称之为“命中”。这时候需要的数据可以直接由缓存提供。对于没有“命中”的数据,需要经过缓存层,进一步从持久化数据层获取。这种情况称为缓存穿透。获取数据后,在将其返回给应用程序之前,我们需要为下一个“命中”查询重新填充缓存。当然,我们上面说的只是针对“读”查询的场景。当应用中数据操作发生变化时,我们需要同时将变化更新到持久层和缓冲层。这时候,我们又会面临一个问题,“先”“后”的问题。“先”和“后”的问题也称为缓存一致性问题。如果先更新缓存,可能会面临持久层更新失败,导致缓存中有脏数据的问题。但是,如果先更新持久层,那么在持久层更新成功到缓存更新之前的这段时间里,我们不得不面临缓存对外提供旧数据的困境。缓存一致性问题,尤其是在高并发环境下,需要根据具体场景进行更细微的控制。比如并发修改的一致性锁;例如,异步刷新的延迟刷新等。缓存和更新我们在上面提到了缓存更新一致性的问题。从实际应用场景来看,又可以细分为强一致性需求、弱一致性需求和最终一致性需求。强一致性需求,如交易状态信息、下单、支付中、支付等应用,需要我们主动及时更新关联,保证交易层面的一致性。包括分布式事务在内的诸多理论应运而生,也为我们解决实际问题提供了很好的实践方案。弱一致性需要一些场景,涉及不太重要的信息更新,并且可以容忍持久层数据和缓存数据在短时间内(例如几分钟)不一致。例如非显式描述信息、统计统计缓存信息等。通常可以采用异步处理。一些短时间(几秒、几分钟)输出固定信息的场景。比如每30s更新一次热点信息、票价信息等。可以通过设置缓存超时和自动淘汰来处理。最终一致性要求保证了数据状态的最终一致性。缓存的粒度就是所谓的粒度,即缓存信息块的级别和大小。选择缓存的粒度取决于我们应用的整体架构、数据存储规划以及具体的应用场景。以用户信息为例,是否应该缓存活跃信息?还是相对静态的信息?它是否缓存在单个属性级别?还是基于整个对象信息?不同的数据粒度也决定了我们存储缓存的形式:整个对象的二进制序列更透明直观的json字符串?属性和值之间的一对一映射?每种形式都有自己的优点和缺点。开发者可以从应用、存储和维护成本等方面进行综合评估和选择。缓存穿透的危害我们前面提到缓存穿透的原因:缓存未命中。那为什么会错过呢?该数据暂时不存在于缓存中。所谓临时可以指数据一开始还没有加载到缓存中,lazyload是按需实时加载应用。也可能是缓存的数据被我们特定的缓存过期策略自动或主动过期。常用的过期策略包括元素个数、内存使用限制和生命周期限制。其实无论是一开始没有加载还是缓存过期或被删除,这些都是我们假设的正常应用场景,同样不做过多评论。数据永远不存在。当对不存在数据的请求到达时,它必须通过缓存到达持久存储层。持久存储的响应能力是有限的。当此类请求达到一定程度时,服务可能面临宕机的危险。至此,我们对缓存作用的理解还需要进一步扩展:减轻下层的负载,保护后端资源。造成这种缓存穿透的原因可以简单分为两个因素:内部应用逻辑问题、外部恶意攻击、爬虫干扰。内部问题好解决,内部观察可预测,良性优化即可;相反,外部问题难以预测,可能需要更加谨慎和多方面的防御性对待。其实无论内部还是外部,缓存层面需要处理的只有一件事:有效拦截渗透。此时,按照通常的惯性思维,第一步是将导致缓存穿透的数据放入缓存中,而不管它是否存在于持久化存储中。比如对于正常删除的用户数据,在缓存层面进行软删除处理,并用状态信息进行标记(我存在,其实我不存在!😳)。此类问题引起的穿透压力可以得到很好的解决。但是,我们也有清醒的认识,真正能造成危害的是异常的入侵数据。比如把穷举遍历的不同数据,一个一个的存入缓存,唯一的结果就是缓存资源溢出耗尽。这是一个相当可怕的场景。对于这种“大数据”攻击,布隆过滤器拦截或许是一个不错的选择。另外说一下缓存雪崩上一节我们说了缓存的承载保护功能。一方面响应速度快,另一方面保护持久层数据。在一些读为主的服务中,缓存几乎承载了90%以上的请求。但是,如果缓存由于某种原因暂时无法提供正常服务,所有的请求都会穿透到持久化存储层,导致存储层极度宕机。那么,我们应该如何应对这种情况呢?高可用缓存的高可用性是防止缓存雪崩的首要保障:主从、读写分离、动态扩展、一致性均衡、异地容灾等。Redis哨兵模式、集群部署等实际应用。:限流、熔断、降级服务治理等服务??治理的目的是什么?服务稳定。限流是对异常流量的控制;融合和降级目标的核心服务资源保护。缓存和持久化数据存储都是资源,或者我们可以从缓存的流控和持久化数据存储的熔断降级保护入手来应对缓存雪崩的发生。缓存元素的集中过期会导致缓存失效。对于设置了过期时间的缓存元素,如果元素同时过期,会有瞬时的外部请求直接到持久化存储层。在实际的缓存应用中,需要采取一定的措施来实现缓存元素过期时间的均匀分布。作者:WindWant编辑:陶佳龙来源:cnblogs.com/niejunlei/p/12914336.html