K:缓存相关问题

缓存的作用在于提高程序的**响应速度**,一般用于程序存储运算所需数据或程序运算后的结果,以便再次访问或运算相同的程序(数据同样相同)时,能够得到快速的响应(适用于**读多写少**的场景)。在现代计算机体系结构中,根据多级存储体系的思想,为解决cpu运算速度与主存读取数据之间的速度不匹配问题,在cpu与主存之间便添加了多级缓存,用于存储部分经常使用到的数据,以此来提高响应速度。对于大容量存储设备—硬盘(磁盘)而言,主存同样可以看成是该设备的缓存。

在一个大型分布式系统中,为减轻数据库的压力,一般而言都会有一个专门的内存空间用于当做数据库部分热点数据的缓存。对于增加了缓存的程序,获取数据的流程一般而言按这样的逻辑进行的:

  1. 访问缓存,当缓存中存在所需数据时,直接返回数据,流程结束。否则进行步骤2

  2. 访问数据库,获取相应的数据进行运算并返回,同时将数据(数据库中拿出的数据或者运算后的结果)存入缓存中,流程结束

这两个步骤在不考虑高并发缓存失效(数据过期/删除了缓存数据)的情况,是没有多大问题的。但是当考虑进这两个情况,缓存中没有相应数据时,便会出现缓存击穿缓存穿透以及缓存雪崩的问题,下面便就这几个问题分别进行讨论,同时给出解决方法。

缓存穿透:

缓存穿透是指缓存和数据库中都没有相应的数据,而大量用户对不存在的数据进行请求访问,而导致所有请求都访问了数据库,让缓存“形若于无”的情况。
该问题会出现在用户删除了数据库中的相关数据后,删除缓存的场景中。也会出现在用户对系统的恶意请求中。

解决办法:

  1. 请求校验;在请求访问数据库之前,先校验请求参数的合法性。避免了非法参数访问数据库的情况(因为非法参数,如id=-1在数据库中是不存在的,为此每次请求都会导致访问数据库),一般将数据库中的值放置在布隆过滤器中,每次请求都先经过布隆过滤器校验,当值不在其中时,就直接返回请求,避免了查库操作

  2. 缓存空值;当访问了数据库中不存在的数据时,可以在缓存中设置对应数据为空值,这样后续访问该数据的请求就会被缓存所拦截而直接返回空值,从而减少对数据库的访问量。但需要注意的是,缓存时间应该设置得短一些,避免新增了数据之后,依然查询不到值的情况

缓存击穿:

缓存击穿是指缓存中没有相应的数据,数据库却中存在相应的数据。大量用户对该数据的请求直接访问了数据库造成的一种现象。
该问题会出现于更新了数据库中的数据之后去删除缓存的数据的场景中。也会出现于首次使用缓存,缓存中没有相关数据的场景中。同时也会出现在某个数据在缓存中失效过期的情况中。

解决方法:

  1. 缓存预热;在缓存启动时,可以对热点数据进行预加载处理。

  2. 加锁,重试;其中一个线程获取锁去访问数据库并将数据放置于缓存行中,另一些线程等待一段时间之后再重试获取缓存中的数据,当不存在时,再度竞争锁。

  3. 热点数据永不过时;不对热点数据设置过期时间,等到更新数据的时候,且下一次查询的时候,再更新数据。

缓存雪崩:

缓存雪崩是指缓存中的大量数据在短时间内失效,导致大量请求直接访问了数据库而导致的一种现象。其可以看成缓存击穿的一种泛化情况,缓存击穿是并发请求同一条失效数据,而缓存雪崩则是不同数据的数据均失效,应用大量请求数据库导致的。

解决方法:

  1. 随机延长数据的缓存时长;对缓存中被访问到的数据随机延长一定的失效时间,这样可以避免数据集中的在短时间内失效。

  2. 缓存进行分布式部署;对缓存做高可用的分布式部署,避免缓存出现单点故障问题导致缓存不可用而应用大量请求数据库。

  3. 限流、降级;当缓存雪崩发生时,为避免大量的请求访问数据库导致系统的不可用,可以采用限流和降级系统的方式,确保系统的可用。

总结:

缓存穿透和缓存击穿这个两个名词有点相近,乍一看不容易辨别,也容易搞混淆,但细细一想其实还是很容易分辨的。缓存穿透和缓存击穿的共同点是缓存中没有相应的数据,而不同点在于缓存穿透在数据库中是没有相应的数据。而对于缓存雪崩,其实际上就是缓存击穿的一种泛化,缓存击穿是针对某一个缓存项失效而言的,而缓存雪崩是针对大量缓存项的。

ps:这篇文章最主要的目的是对上一篇文章中关于缓存使用可能会出现的相关问题的总结。通篇是理论有关的内容,在此仅做个记录。


这个是本人的公众号,致力于写出绝大部分人都能读懂的技术文章。欢迎相互交流,我们博采众长,共同进步。

公众号二维码

posted @ 2020-04-01 15:15  林学徒  阅读(194)  评论(0编辑  收藏  举报