缓存穿透、缓存击穿、缓存雪崩
Redis - 缓存穿透、缓存击穿、缓存雪崩
为啥要有缓存
每次请求数据,都直接达到数据库上,数据库受不了,所以得在请求和数据库中间加个缓冲带。这个缓冲带需要非常快,所以就放在内存中,和 CPU 离得近,数据拷贝等操作会很快。然后就先抵挡一部分请求,直接在缓存中就能获取到部分数据,如果没命中缓存再去数据库查,也能减少很大一部分请求,尤其是针对热点数据,能减少 80%以上的请求。
缓存穿透
场景
有个黑客,一直用 id=-1
这个条件来不断向服务发起请求,但是这个 id 在数据库中并不存在,不断的发起请求,请求就会穿过缓存落在数据库中,由数据库来进行查询,这就是缓存穿透!缓存穿透无法避免,只能尽量避免高频的缓存穿透。
解决办法
1、创建空缓存对象
当请求在缓存中未被命中,同时在数据库中也没有查询到数据,则直接创建一个空的缓存对象,当相同的恶意请求再次高并发袭来,可以直接使用空的缓存对象。不过这有个问题,就是一旦 id 被批量的增加或者减少,都是你数据库不存在的数据,那就会创建非常多无效的缓存数据,压垮内存。
2、使用布隆过滤器
Bloom Filter过滤器是一个占用空间很小,效率很高的随机数据结构,由一个 bit 数组和一组 hash 算法构成,可以用于判断一个元素是否在一个集合中,查询效率很高,并且内存占用也很小。
布隆过滤器也存在hash 碰撞问题,会有一定的错误率。但是针对一个数据,布隆过滤器说存在不一定存在,但是布隆过滤器说不存在那就一定不存在。一旦被判定缓存数据不存在了,那就直接查询数据库创建缓存数据就可以。
缓存击穿
场景
当你出现缓存穿透的情况后,一旦高并发的请求过来,会造成缓存击穿的情况。缓存击穿其实是缓存穿透的一种特殊表现。
例如微博的例子,某明星的热点数据,平时常规访问,但是一旦发布了结婚等劲爆消息后,会有非常多的吃瓜群众涌现出来去访问同一个热点数据,一旦这个热点数据过期失效后,就会将所有请求打到数据库上,直接把数据库拖垮。
解决办法
1、热点数据永不过期
将热点数据设置为永不过期,那么就不会出现缓存击穿的情况,但是会大量占用内存,并且一旦数据库的数据有了变化,没有及时更新到缓存中,就会出现数据不一致的情况。所以需要一种方法来保证缓存和数据库的强一致性,如果业务对于数据的一致性要求不高,保持弱一致性也可以。
2、分布式锁
分布式的业务访问同一个 redis,达到分布式锁的目的。所有线程竞争锁资源,一个线程拿到锁资源以后,如果热点数据过期或者被删除了,重新访问数据库拿到数据然后进行缓存,释放锁以后再次竞争到锁资源的线程就能直接访问缓存数据了。
缓存雪崩
场景
缓存雪崩也是缓存穿透的一种特殊表现,一个明星宣布劲爆消息后一个热点数据过期或者被删除可能会导致缓存击穿,好几个甚至上百个明星同时宣布了劲爆消息然后这些热点数据都同时过期或者被删除,那缓存就直接血崩了,超高并发的请求打到了数据库中,数据库当场崩溃。
还有一种情况,redis 服务直接崩溃了,那所有的请求也会直接打到数据库上,导致数据库崩溃。
解决办法
1、热点数据设置随机过期时间
热点数据设置随机过期时间,保证不会出现大批量热点数据同时失效、过期或者被删除的情况出现。
2、redis 集群
没有什么是加机器解决不了的,如果有那就继续加。所以为了防止单个redis 服务挂掉拖累整个服务,直接搞个 redis 集群,一个挂了还有另外的可以继续使用。
总结
综上所述的问题都是很典型的缓存问题,也同时提出了解决办法,但是解决办法都是针对单个情况去解决问题,还会出现其他的衍生问题,所以需要同时去解决衍生问题。比如创建了非常多的空缓存对象,这个时候需要有一个线程去监控这些缓存对象,并进行删除。或者创建的空缓存对象设置比较短的过期时间,也可以让 redis 自己去删除。方法多种多样,可以根据实际情况去选择使用哪一种。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!