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

架构师面试经常考!缓存三大问题及解决方法!

时间:2023-03-21 11:56:19 科技观察

1。缓存的原因随着互联网系统发展的逐步完善,以及系统qps的提高,目前的系统大多加入了缓存机制,避免直接操作数据库的请求过多,造成系统瓶颈,大大提高了用户体验和系统稳定性。2、缓存问题缓存的使用虽然给系统带来了一些质的提升,但同时也带来了一些需要注意的问题。(1)缓存穿透缓存穿透是指查询一个一定不存在的数据,因为缓存中没有数据的相关信息,所以会直接去数据库层查询,从系统层面上看,好像是穿透了缓存层是直接到db的,叫做缓存穿透。如果没有缓存层的保护,这种对一定不存在的数据的查询可能会对系统造成危险。如果有人恶意利用这些本不该存在的数据来频繁请求系统,不,准确的说是对系统的攻击,请求会到达数据库层,导致db瘫痪,导致系统故障。(2)解决方案缓存穿透业界解决方案比较成熟,常用的主要有以下几种:Bloomfilter:一种类似于哈希表的算法,利用所有可能的查询条件生成位图,执行数据库这个位图会在查询前用于过滤,如果不在里面就直接过滤,从而减轻数据库层面的压力。布隆过滤器算法是在番石榴中实现的。空值缓存:一种相对简单的解决方案。第一次查询到不存在的数据后,将key和对应的null值放入缓存,但是设置的过期时间要短一些,比如几分钟,这样可以应对大量的攻击在短时间内在这个键上。设置较短的过期时间是因为这个值可能与业务无关,意义不大,而且这个查询可能不是攻击者发起的。长期存储是必要的,因此它可以提前过期。(3)缓存雪崩在redis、memcache等普通的缓存系统中,我们都会给缓存设置一个过期时间,但是如果所有缓存的过期时间都一样,那么所有的系统请求都会同时失效。发送到数据库层,db可能无法承受如此大的压力而导致系统崩溃。(4)解决线程互斥:只让一个线程建立缓存,其他线程等待建立缓存的线程执行完毕,再从缓存中重新获取数据。每一时刻只有一个线程在执行请求,从而减轻了db的压力。但是缺点也很明显,降低了系统的qps。错开到期时间:这种方法比较简单粗暴。由于同时失败会导致太多请求雪崩,所以我们可以通过错开不同的过期时间来从一定长度上避免这个问题。在设置缓存的过期时间时,可以使用来自适当值字段的随机时间作为失败时间。(5)缓存击穿缓存击穿实际上是缓存雪崩的一种特例。用过微博的人应该都知道,微博有一个热门话题的功能,用户对热门话题的搜索量往往会在某些时刻大幅增加。它高于其他主题。这样的我们成为了系统的“热点”。由于系统中这些热点的数据缓存也有过期时间,当热点的缓存达到过期时间后,仍然可能会有大量的请求到达系统,如果没有缓存层的保护,这些请求也会到达db,可能会导致失败。breakdown和avalanche的区别在于breakdown是针对特定的热点数据,而avalanche是针对所有数据。(6)方案二级缓存:对热点数据进行二级缓存,对不同级别的缓存设置不同的过期时间,使得请求不会直接穿透缓存层到达数据库。这里参考阿里巴巴的双11万亿流量缓存击穿方案。解决这个问题的关键在于热点接入。由于热点可能会随时间变化,固定数据的特殊缓存无法解决问题。结合LRU算法可以更好的帮助我们解决这个问题。那么什么是LRU,下面粗略介绍一下。LRU(Leastrecentlyused,最近最少使用)算法根据数据的历史访问记录来剔除数据。其核心思想是“如果数据最近被访问过,那么未来被访问的概率也更高”。最常见的实现是使用链表来保存缓存数据。如下图所示,这个链表就是我们的缓存结构。缓存处理步骤是先将新数据放入链表头部。在数据插入过程中,如果检测到链表中有数据被再次访问,即有再次访问该数据的请求,则插入链表头部,因为它们可能是比较热点的数据其他数据,并且它们具有更长的保留时间。最后,当链表数据放满时剔除最下面的数据,也就是不经常访问的数据。LRU-K算法。其实上面的算法也是这个算法的一个特例,即LRU-1。算法在这个过程中得到了改进。例如,意外的数据冲击会导致命中率低。比如某个数据即将触底,即将被淘汰。但是,由于请求并放入头部,之后就没有这样的数据了。请求,那么数据的继续存在其实是不合理的,LRU-K算法对于这种情况有更好的解决方案。结构图如下:LRU-K需要维护一个或多个队列来记录所有缓存数据被访问的历史。只有当数据访问次数达到K次时,才将数据放入缓存中。当需要淘汰数据时,LRU-K会淘汰当前时间第K次访问时间最大的数据。第一步添加数据还是放入第一个队列的头部。如果队列中的数据没有达到K次(该值根据具体系统qps确定),则继续到达链表底部,直到被淘汰;如果数据在队列中,访问次数达到K次,则将其加入到下一个二级链表中(具体来说,几级结构也是结合系统分析的),接下来的操作二级链表在二级链表中按时间顺序排列。如果再次访问链表中的数据,则移动到头部。当链表满了,最下面的数据就会被淘汰。与LRU相比,LRU-K需要维护一个额外的队列来记录所有缓存数据被访问的历史,因此需要更多的内存空间来构建缓存。但优点也很明显。它降低了数据污染率并提高了缓存命中率。也是系统用一定的硬件成本换取系统性能的一种方式。当然还有更复杂的缓存结构算法,可以点开LRU算法了解,比如TwoQueues和MutilQueues等,本文不做赘述,只是给读者提供一个解决方案。