本篇对缓存三剑客问题进行介绍和解决方案说明,下篇将进行实践,有需要的同学可以跳转下篇查看实践篇:(待发布)
缓存三剑客是什么?
缓存三剑客指的是在分布式系统下使用缓存技术最常见的三类典型问题。它们分别是:
-
缓存穿透:请求不存在的数据。
-
缓存击穿:热点数据突然过期。
-
缓存雪崩:大量数据同时过期。
三种问题都会导致缓存失效,对数据库造成巨大压力,可能会导致数据库崩溃,引发系统性崩溃。所以说,学会如何预防和解决这三种问题显得尤为重要。
缓存穿透问题
问题描述
缓存穿透问题指的是:当用户访问的数据即不在缓存中,又不在数据库中,也就是访问系统中不存在的数据,导致请求在访问缓存的时候发现不存在缓存,持续的去访问数据库。并且在访问数据库后,因该数据不存在导致无法构建缓存。若存在大量该请求时,数据库压力过大,可能会导致数据库崩溃。
用一句话来说:用户请求系统中不存在的数据,导致无法构建缓存,持续访问数据库,导致系统压力过大。
可能产生的原因
一般情况下,在我们的系统中不应该存在这种可以访问不存在的数据的方法。大概率是不好的用户进行恶意请求。比如一个用户了解到了系统中的一个API的访问方法,那么用户可以通过构建出来一个不存在的数据,然后去持续压力访问该API,就会导致缓存穿透问题的发生。
解决方案
明确一下我们需要解决的问题:我们要解决因为缓存穿透问题导致的数据库压力过大。那么换句话来说,我们只需要将这些无效数据尽可能的在访问数据库之前屏蔽掉就好了。
方案1:缓存空数据
如果我们发现一个API请求的数据在我们的数据库和缓存中都不存在的话,我们可以通过将这个无效数据也缓存到Redis中,若恶意请求的key值都是一致的话,就会被我们拦截在缓存,防止数据库压力过大。
该方案也存在一些问题:
-
如果恶意请求的数据是通过一个方式构造出来的,构造出来了大量的无效数据,那我们使用该方法可能会导致Redis中存取了大量的无效数据,这其实对Redis来说也不是很友好的。所以我们需要为这些缓存也要添加一个TTL过期时间,让Redis将这些数据在一段时间之后进行过期。需要注意的是,这个位置如果我们的过期时间设置不合理的话,也可能会出现我们后文会出现到的问题:缓存雪崩问题。
方案2:布隆过滤器
布隆过滤器是一个比较好用的数据结构,通过布隆过滤器,我们可以快速判断一个数据是否存在我们书库中。
布隆过滤器简单来说是由两部分组成的:一个二进制向量(也可以说是个Bit Array二进制数组)和一系列的哈希函数。
布隆过滤器通过一个较大的bit数组用于保存所有数据,数字中每个数组只占1bit,并且每个元素的表示只能是0或1用于表示是否存在。当我们添加元素的时候,布隆过滤器通过一系列的哈希函数将数据进行计算,计算后的结果映射到数组中,将对应位置映射为1。当查询元素的时候,通过哈希函数将元素映射,查看对应位置是否均为1,如果都为1的话则表示元素可能存在我们的数据中。
需要注意的是:如果我们查询数据映射均为1的时候,我们只能说这个元素可能存在。因为存在多个元素映射到同样的一系列位置的情况(哈希冲突)。所以我们不能一定确定数据存在。但是我们至少可以确定,如果映射的位置存在一位以上的0,那么可以说明这个数据一定不存在。
缓存击穿问题
问题描述
缓存击穿问题指的是:当我们处于一个高并发的情况下,存在一个热点数据的访问量非常大,在我们依然存在大量请求的同时这个热点数据的缓存突然失效(可能是过期了或者被删除),导致大量的并发请求同时穿透缓存,直接访问数据库,导致数据库压力过大,可能会导致系统崩溃。
用一句话来说:热点数据过期,导致大量并发请求穿透缓存,直接访问数据库。
可能产生的原因
假设我们商城系统对某个商品的有秒杀活动,在秒杀商品活动结束的时候缓存过期,导致大量的后续请求直接访问到数据库中。
本质上就是热点数据在高并发期间缓存过期。
解决方案
方案1:加互斥锁
通过在缓存失效后,添加一个互斥锁,保证只有一个请求去查询数据库,然后更新缓存,这样就可以保证后续的请求可以正确的访问缓存,不会直接访问数据库导致数据库压力过大。
方案2:提前预热热点数据
在发布前,我们应该对系统中存在的热点数据应该有预知的。我们可以针对热点数据进行提前预热,将其存入缓存中并设置合适的过期时间。
方案3:设置热点数据永不过期
该方案在选用的时候需要考虑实际情况,如果我们这个热点数据是一致会被访问,也就是说这个热点数据一直都是热点数据的话,我们可以考虑设置这个热点数据永不过期。如果,热点数据是有失效性的,那么这个方案并不很适用。
缓存雪崩问题
问题描述
缓存雪崩问题指的是:当我们存在系统中大部分缓存在同一时间失效,那么在缓存重建的过程中,如果存在大量的请求,这些请求会越过缓存直接对数据库进行访问,对数据库造成巨大的压力,可能会导致系统崩溃。当然,缓存服务器若突然宕机了,也会造成缓存雪崩问题。
可能产生的原因
-
大量Key的TTL结束时间一致:我们设置缓存过期时间的时候可能设置的并不是很合理,导致大量的缓存在同一时间过期了,导致缓存雪崩问题。
-
缓存服务器宕机:缓存服务都没了,所有请求肯定都会直接对数据库进行访问,形成缓存雪崩问题。
解决方案
对于可能造成缓存雪崩的两种原因,有下面不同的解决方案:
方案1:设置随机失效时间
通过为每个需要添加随机过期时间的Key添加随机的失效时间偏移量,避免在同一时间内有大量数据过期。
该方案可以解决大量Key同一时间过期的情况,但是有效性也比较有限,如果存在大量数据同时添加缓存的话,该方案可能有些治标不治本。
方案2:采用多级缓存架构
通过构建多级缓存,不同层级设置不同的过期时间策略,保证同一时间内,存在可用的缓存。
方案3:部署集群
通过部署Redis集群,防止因为单个缓存宕机导致服务不可用。
总结
本篇对于缓存三剑客问题进行说明,在下篇文章将对各个问题以及对应的解决方案进行实践。
【Redis】缓存三剑客问题实践(下):(待发布)