缓存击穿,穿透和雪崩
背景
首先说一下为什么会写这片文章,因为这个对我来说是印象非常深刻的,那是还在实习的时候,当时接了一个任务(其实就是练手的),大致需求是写一个白名单,然后有一个功能对白名单开放。因为是新功能,需要在部分地区试点,如果没有问题才会放开到全国城市运行。就是这么一个小的功能,让当时的我,呵呵。我记得那是第一次使用Redis,根本不知道什么是缓存雪崩啊,缓存击穿啊,穿透呀这些,还有更可怕的缓存一致性问题(TODO 下期再说)。 当时团队十几位大佬review我的代码,哼。这就是为什么印象会深刻一些吧,关键那是我第一个review代码,还是第一次使用redis,整个review就像是十几个大佬在面试我。真是怀疑人生了。。我是废物,别笑。
1 | // 大致伪代码 |
我上面的例子其实就是典型的缓存穿透的问题。因为仅仅是开放了十几个城市来试点功能,所以大部分的查询都是缓存不命中的。
下面引出今天我们的三个关键词:
缓存穿透
要查询的数据,缓存中基本上没有,所以大概率情况下缓存是不命中的,而是去数据库中去查询数据,导致缓存相当于是一个摆设。如果Key不是热点访问还可以,如果是热点Key,而且并发量也会很大的情况下,绝大多数的请求都会打到数据库上,很容易造成数据库宕机。
解决方案
1、布隆过滤器进行校验,bloom filter典型应用场景(用户名是否存在,黑名单机制,单词错误检测)
2、缓存空值方法,这个网上也是说的比较多的,这种方式也是可以的
在此我说一下我的解决方案:
1、校验一个城市是否是城市白名单的城市时,直接查询缓存,存储的数据结构是使用的hash;
2、如果没有查到这个城市,则认为这个城市不在白名单中,因为我是先查的hash,然后在查询hash中的具体城市。如果hash的值都没有查到,那说明缓存失效了;
3、针对缓存失效的情况,可以再查DB来更新缓存,这个时候有人要较真了,会不会出线缓存击穿的情况呀,这个看具体场景,因为我的业务是不需要的,所以这里就直接查库更新缓存了,如果需要的话,那可以上缓存击穿的解决方案,或者看看上面两种解决方案有没有合适的,哈哈哈;
4、因为城市是在后台进行配置的,所以我是在增删改的操作时,保证了缓存数据的一致性的前提下,才选择相信缓存的。这是我当时的解决方案。
缓存击穿
某个热点Key在一段时间内失效了,此时有大量请求瞬时抵达,会严重增加数据库的压力。因为我们的缓存数据一般都是要设置过期时间的,当缓存失效时,会去查询数据库同时更新缓存数据。
解决方案
1、可以加锁,来保证只有第一个请求进来时达到数据库上,然后更新缓存,第二个请求进来是就会命中缓存,当然如果是分布式服务,那就需要使用分布式锁了。
2、合理设置缓存时间,可以将热点Key时间设置长一些,或者根据业务将失效时间设置在业务量比较小的波段,都是可以的。有的解决方案会不设置Key的过期时间,这个,看情况吧,不建议这样。 如果大量的key不设置过期时间,则长期占用内存也是不好的。
缓存雪崩
缓存雪崩顾名思义,就是大量Key在一段时间或者瞬时失效,或者Redis服务重启(所有Key失效,因为是存在内存中),从而导致大量请求打到数据库上,增加数据库压力
解决方案
1、将热点key设置不同波段的过期时间,把过期时间散列开。
2、也可以使用分布式锁来限制高并发的请求,和缓存击穿的解决方案同理。
3、对于Redis重启或宕机的问题,可以考虑集群部署,并保证数据的同步和一致性;
生活远不止眼前的苟且