缓存穿透
什么是缓存穿透
缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。
缓存穿透的大致流程
如下图所示,用户的请求最终都要跑到数据库中查询一遍。
解决办法
最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。
1)缓存无效 key
如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间,具体命令如下: SET key value EX 10086
。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求 key,会导致 Redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。
另外,这里多说一嘴,一般情况下我们是这样设计 key 的: 表名:列名:主键名:主键值
。
2)布隆过滤器
布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在于海量数据中。我们需要的就是判断 key 是否合法,有没有感觉布隆过滤器就是我们想要找的那个“人”。
具体是这样做的:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。
加入布隆过滤器之后的缓存处理流程图如下。
但是,需要注意的是布隆过滤器可能会存在误判的情况。总结来说就是: 布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
为什么会出现误判的情况呢? 我们还要从布隆过滤器的原理来说!
我们先来看一下,当一个元素加入布隆过滤器中的时候,会进行哪些操作:
- 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。
- 根据得到的哈希值,在位数组中把对应下标的值置为 1。
我们再来看一下,当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行哪些操作:
- 对给定元素再次进行相同的哈希计算;
- 得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
然后,一定会出现这样一种情况:不同的字符串可能哈希出来的位置相同。 (可以适当增加位数组大小或者调整我们的哈希函数来降低概率)
缓存击穿
什么是缓存击穿
缓存击穿是指在缓存系统中,某个被大量访问的热点数据在缓存中的有效期刚好到期,而此时大量并发请求同时过来访问该数据,由于缓存中已无此数据,这些请求就会同时绕过缓存直接去访问数据库,从而导致数据库瞬间承受巨大的查询压力,可能会影响数据库甚至整个系统的正常运行。
解决方案
常见的解决方案有两种:
互斥锁
互斥锁的解决思路就是在Redis进行缓存重建时,拿到一个互斥锁,其他请求拿不到这个锁就是乖乖等待锁的释放。
逻辑过期
逻辑过期的解决思路如下:
在存入redis的value中增加一个字段,该字段为过期时间加上x分钟,通过计算就知道这个数据是否逻辑上过期,事实上没过期一直存在redis中。
在redis进行缓存重建的时候,会另开一个线程进行重建并拿到互斥锁,其他线程拿不到数据想要缓存重建时也拿不到锁,那就直接返回旧数据。
互斥锁方案:由于保证了互斥性,所以数据一致,且实现简单,因为仅仅只需要加一把锁而已,也没其他的事情需要操心,所以没有额外的内存消耗,缺点在于有锁就有死锁问题的发生,且只能串行执行性能肯定受到影响
逻辑过期方案: 线程读取过程中不需要等待,性能好,有一个额外的线程持有锁去进行重构数据,但是在重构数据完成前,其他的线程只能返回之前的数据,且实现起来麻烦
缓存雪崩
什么是缓存雪崩?
实际上,缓存雪崩描述的就是这样一个简单的场景:缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求。 这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。
举个例子:系统的缓存模块出了问题比如宕机导致不可用。造成系统的所有访问,都要走数据库。
还有一种缓存雪崩的场景是:有一些被大量访问数据(热点缓存)在某一时刻大面积失效,导致对应的请求直接落到了数据库上。 这样的情况,有下面几种解决办法:
举个例子 :秒杀开始 12 个小时之前,我们统一存放了一批商品到 Redis 中,设置的缓存过期时间也是 12 个小时,那么秒杀开始的时候,这些秒杀的商品的访问直接就失效了。导致的情况就是,相应的请求直接就落到了数据库上,就像雪崩一样可怕。
解决办法
(1)给不同的Key的TTL添加随机值(推荐)
操作简单,当我们在做缓存预热的时候,就有可能在同一时间批量插入大量的数据,
那么如果它们的TTL都一样的话就可能出现大量key同时过期的情况!!!
所以我们需要在设置过期时间TTL的时候,定义一个范围,追加该范围内的一个随机数。
(2)利用Redis集群提高服务的可用性
使用集群提高可靠性
(3)给缓存业务添加降级限流策略
微服务的知识
(4)给业务添加多级缓存
请求到达浏览器,nginx可以做缓存,未命中找Redis,再未命中找JVM,最后到数据库......
总结
我个人感觉redis的缓存穿透和缓存击穿有部分是类似,而缓存击穿和缓存雪崩又有一部分是类似的
缓存穿透和缓存击穿的大至区别
- 缓存击穿:指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
- 缓存穿透:指查询一个根本不存在的数据,缓存层和存储层都不会命中,每次都会去请求数据库,若有大量这样的请求,可能会导致数据库压力过大甚至崩溃。
缓存击穿和缓存雪崩的大至区别
- 涉及数据范围:缓存击穿主要针对的是单个热点数据,是由于单个关键数据的缓存过期,大量请求同时访问这一数据而引发问题;而缓存雪崩涉及的是大量数据,是大量缓存数据在同一时间或短时间内集体失效,导致大量请求冲向数据库。
- 引发原因侧重:缓存击穿更多是因为某个热点数据的缓存时间设置不当,或者在缓存过期的瞬间有大量并发请求访问;缓存雪崩除了可能因为缓存时间设置不合理外,还可能由于缓存服务器故障、大量数据同时更新等原因导致大量缓存数据同时失效。
- 问题表现形式:缓存击穿是大量请求集中访问某一个特定数据;缓存雪崩是大量请求分散地访问多个不同的数据,但这些数据的缓存同时失效,导致整体请求流量对数据库造成巨大冲击。
文章参考
Redis的缓存穿透、缓存雪崩、缓存击穿问题及有效解决方案_缓存雪崩和缓存穿透问题解决方案-CSDN博客