个人原创:我的博客文章链接
一、缓存雪崩
什么是缓存雪崩呢?
缓存雪崩,是指同一时间段大量的缓存key同时到期(失效)或者Redis服务宕机,导致大量的请求(如果是大型电商平台如某宝、某东,甚至可以说是海量请求)瞬间到达数据库,这会给数据库带来巨大的压力。
下图是正常情况下,请求访问到Redis缓存,Redis收到请求并响应数据给客户端,只有部分在Redis中未做缓存的会到达数据库。
如果大量的缓存key过期,导致请求未命中Redis,大量的请求就会到达数据库,导致数据库压力剧增。
另外一种情况就更糟糕了,Redis宕机了。缓存key过期还只是部分请求,而不是全部的请求到达数据库,而Redis宕机的话,那就是所有的请求瞬间到达数据库。
解决方案:
针对以上两种情况,我们也有相对应的解决方案。
给不同的缓存key的过期时间(TTL)添加随机值
主要是防止在同一时段有大量的key同时失效利用Redis集群提高Redis服务的可用性,即高可用
利用集群,哨兵机制,监控Redis主从的状态,如果发现主服务器宕机,马上选择一个从机作为新的主机,确保Redis能一直对外提供服务,另外主从还能实现数据的同步,确保数据的一致性,保证了Redis服务的高可用。给业务缓存添加降级限流策略
在发生了重大的错误,短时间无法解决,我们就可以提前做好一个容错方案,比如说给服务降级,限制流量,甚至拒绝服务。虽然牺牲了一些服务,但是最终保护了数据库。给业务添加多级缓存
比如浏览器缓存,对页面进行缓存,对JVM添加缓存等等,做一个多层级的缓存。
二、缓存穿透
什么是缓存穿透?
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存就永远不会生效,这些请求都会打到数据库。
请求的数据不存在,可能数据是瞎编的,也可能是记错了或者打错了,但是这些请求都会一直到达数据库。
如果这时一个很坏很怀的人,故意一直请求不存在的数据,很有可能就会搞垮我们的数据库。那我们该怎么办?
解决方案:
1.缓存空对象
请求的数据在数据库中也不存在时,数据库会把一个空的值缓存到Redis,这样下次请求不存在的数据时,就会直接命中Redis,不会到达数据库
优点:简单粗暴,方便
缺点:
- 造成额外的Redis内存消耗
- 还有可能造成短期的数据不一致
针对缺点,我们也可以有对应的实现来缓解。
可以在缓存空值时给其设置一个TTL,到期自动删除,缓解内存消耗
对于短期的数据不一致问题,我们可以给TTL设置的时间短一点,或者在数据库中的数据更新之后,立马缓存到Redis中
2.布隆过滤
在客户端和Redis之间加一层布隆过滤器,当客户端发送请求时,会先到布隆过滤器进行判断,如果请求的数据不存在,会直接拒绝;如果存在,再放行到Redis
布隆过滤器的实现原理可以看成是Map,存储的是二进制位,是数据库中的数据根据某一种哈希算法得到的哈希值,再将得到的哈希值返回二进制位。
个人理解,如果有偏差,欢迎指正!
优点:
内存占用较少,没有多余的key
缺点:
- 实现复杂
- 存在误判可能
如果布隆过滤器判断该请求的数据不存在,那就是不存在,直接拒绝;如果判断存在,也有可能是误判,请求依然会打到数据库,仍然会有小的穿透风险。
三、缓存击穿
什么是缓存击穿?
缓存击穿问题,也叫热点key问题。缓存雪崩是大量的key过期;而缓存击穿,则是部分key过期。
过期的key是被高并发访问并且缓存重建业务较复杂的热点key。同样也是会导致无数的访问请求瞬间到达数据库带来巨大压力。
比如,常见的秒杀场景,缓存的秒杀就是热点key,在同一时段有大量用户在访问,并且秒杀的缓存重建起来也比较复制。如果该热点key失效,这些请求都会打到数据库,导致服务器不稳甚至崩溃。
解决方案:
互斥锁
优点:
- 没有额外的内存消耗
- 保证一致性
- 实现简单
缺点:
- 线程需要等待,性能受影响
- 可能会有死锁风险
逻辑过期
优点:线程不需要等待,性能较好
缺点:
- 无法保证一致性
- 有额外的内存消耗
- 实现复杂