缓存雪崩、穿透、预热、倾斜、缓存穿刺/击穿、更新、降级等

一、缓存雪崩

  1. 由于**原有的缓存集体性的在同一时间段内过期失效,而新缓存未到期间,所有原本应该访问缓存的请求都去查询数据库了**,这会对数据库造成巨大压力,会导致数据库崩掉。
  2. 从而形成一系列连锁反应(雪崩),一直造成整个系统崩溃!
  3. 缓存正常从Redis中获取,示意图如下:

缓存雪崩、缓存穿透、缓存预热、缓存倾斜、缓存穿刺击穿、缓存更新、缓存降级 等 - 图1

缓存集体性的失效瞬间,示意图如下:

缓存雪崩、缓存穿透、缓存预热、缓存倾斜、缓存穿刺击穿、缓存更新、缓存降级 等 - 图2

    缓存失效时的雪崩效应对底层数据库系统的冲击非常可怕!

解决方案一:
    大多数系统设计者考虑:**用加锁或者队列的方式,来保证同一时间内不会有大量的线程**,同时对数据库进行读写分离,从而避免 数据库崩溃掉。

解决方案二:
    还有一个简单方案就是:**将缓存的失效时间分散开**,这样每一个缓存的过期时间的重复率就会降低,**就很难引发大量的缓存集体失效的事件。**

二,缓存穿透

    缓存穿透是指:数据库没有这个数据,缓存中也没有。

    这样就导致每次查询数据的时候,在缓存中找不到,都要去数据库中再查找一遍。

    这样每次查询都需要到数据库,无故的剧增了数据库的压力。

解决方案一: 缓存空对象
    如果一个查询返回的数据为空,我们仍然**把这个空结果进行缓存**,这样用户请求第二次到缓冲中获取,就有值了,而不会继续访问数据库。

解决方案二:采用布隆过滤器
    布隆过滤器是一个由一个很长的二进制向量和一系列随机映射函数构成的概率型数据结构,这种数据结构的空间效率非常高,可以用于**检索集合中是否存在指定的元素**。

    一个可能存在的元素才会通过该过滤器,**一个一定不存在的数据会被这个过滤器给拦截掉**。

原理:布隆过滤器是一个 由一个长度为 m 比特的位数组 与 k 个哈希函数 组成的数据结构。当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就大约知道集合中有没有它了,也就是说,如果这些点有任何一个为 0 ,则被检元素一定不存在;如果都是 1 ,则被检元素很可能存在。

至于说为什么都是 1 的情况只是可能存在检索元素,这是因为不同的元素计算的哈希值有可能一样,会出现哈希碰撞,导致一个不存在的元素有可能对应的比特位为 1。


三,缓存击穿/穿刺

    简单来说,**缓存击穿是指:一个热点key**在不停的扛着高并发,当这个热点key 在失效的瞬间,大量的并发请求就穿破缓存,直接请求数据库,数据库的查询压力就会立即骤增、甚至崩溃掉。

解决方案一,设置这个热点数据永不过期;

解决方案二,给数据库加互斥锁;
    缓存中没有数据,那么想要访问数据库的多个线程,需要先获取到锁才能从数据库中去取数据,第一个线程没释放锁之前,其他的线程都得在外边等待。

    然后第一个线程从数据库中取到了一次数据之后,顺手更新一下缓存,于是其他的线程就可以再次去缓存中请求数据就行了。

三,缓存预热

    缓存预热就是系统上线之前,**将一些缓存数据提前加载到缓存系统中**。这样就可以让用户直接查询事先被预热好的缓存数据!不会去访问数据库了。

四,缓存更新

    除了缓存组件自带的缓存失效策略之外,我们还可以根据具体的业务需求,进行自定义的缓存淘汰,常见的策略有两种:
  • (1)定时去清理过期的缓存;

  • (2)当有用户请求过来时,先判断这个请求所用到的缓存是否过期,过期的话就去数据库中得到新数据,同时并更新到缓存中。


五,缓存降级/服务降级

    当访问量剧增、服务出现问题时,仍然**需要保证核心的服务还是可用的**,这时可以采用关闭一些非核心的服务的方式,来保证核心服务可用。即,丢卒保帅。

    降级的最终目的是保证核心服务可用。

六,缓存倾斜

    缓存倾斜又称为:热点key倾斜。

    即,缓存中存在这个key,但是由于这个key突然的成为高热点key,(比如明星出轨),

    这样将导致大量的用户请求突然高并发的访问这个高热点key所在的那台缓存服务器,最终导致那台缓存服务器崩掉,

    继而请求又到达下一个缓存服务器,下一个缓存服务器又承受不住而崩掉,最终导致整个缓存模块崩掉。

解决方案一:
    在 Redis 集群的基础上,基于Redis主从结构读写分离的特点,**增加从节点的数量—提高读操作的并发能力**,从而缓解缓存模块的压力。

解决方案二:
    我们可以**将这个热点key复制出多个子key**,每个子key的value值一样,查询的时候使用hash取模算法,**将压力分摊到不同的服务端节点。**