缓存雪崩

按照目前行业内的普遍做法,高访问量的首页和热点数据都会去做缓存,缓存一般是定时任务去刷新,或者查不到结果去刷新的,但是定时任务有这么一个问题:

如果redis有大量热门数据的key失效时间都是同一时间,那么到了失效的时间点,大量流量爆发,假设有1w个请求,而这些失效的key原本能抗住8000个请求,但是缓存的key失效了,此时1秒内1w个请求全部落到DB上,数据库必然扛不住,可能连报警没有,就直接挂了,这个时候没有太好的解决方案,直接重启数据库,这个期间因为数据库挂了,redis的缓存也没办法更新,重启后的流量还是直接打到数据库上,继续崩。这就是redis的缓存雪崩。

就算SQL优化的再好,分表也做了优化,但是跟缓存的速度还是有很大差距的。

举个例子,如果是采用hash运算后对缓存服务器数量进行取模的做法,在缓存服务器数量发生变化时,就会引起缓存雪崩。

总结:缓存雪崩就是同一时间大面积缓存失效,请求直接落到数据库层面,如果没做熔断等策略,导致整个服务挂了。

那么应该如何解决这种问题呢?

  • 在批量往redis存数据时,给每个key的过期时间加上随机值就好了,保证同一时间不会大面积缓存失效。
  • 如果是redis集群,将热点数据均匀的分布在不同的redis库里也能避免大面积失效的问题

缓存穿透和缓存击穿呢?它们和缓存雪崩又有啥区别吗?

先说缓存击穿,它跟缓存雪崩比较类似,也是key失效,但是不同的是,缓存击穿是某个或小部分的热点key失效,在这个key失效的瞬间,持续的高并发就击穿了缓存,直接请求数据库,就像一个完整的盾牌,被子弹击穿了一个洞一样。所以,缓存击穿也叫缓存并发

缓存穿透又是怎么回事呢?

缓存穿透是指查询一个缓存和数据库都不存在的数据,而用户不断的发起请求,比如用户请求一个id=-1或者是个很大的不存在的数据,这种用户很可能是个攻击者,瞬间的大量请求不存在的数据会导致服务器压力过大,严重的话,会击垮数据库。

这两种情况又有什么解决办法呢?

缓存击穿的话,几个解决方案供参考

  1. 设置热点数据缓存不过期,用定时任务去更新缓存;
  2. 设置分级缓存配合互斥锁,可采用2级缓存的方式缓存热点数据。L1缓存过期时间短,L2缓存过期时间长,请求优先落到L1缓存上,如果L1缓存未命中则加锁,这个时候只有一个线程获取到锁,这个线程再去数据库获取最新的数据更新到L1和L2缓存中,其它线程依旧从L2缓存获取数据并返回。这个方案需要在业务能容忍更新缓存时存在短时间L2缓存上数据不一致的情况;
  3. 加锁,就是在获取数据时,缓存不存在,通过加锁的方式获取数据库的值,这样能保证在访问数据库只有一个请求过来,从而保证了数据库的负载。

缓存穿透可以直接在接口层面做校验,用户鉴权校验,参数合法性校验,校验不通过直接return。网关层也做拦截,单个IP在短时间内大量请求,超过阈值直接拉黑。

还可以采用布隆过滤器(Bloom Filter)来防止缓存穿透。原理很简单,就是通过高效的算法和数据结构快速计算出查询的数据是否在数据库中存在,不存在直接return,存在就刷新key-value再return。

不推荐写空数据到缓存,就算增加这些空数据的过期时间,也会增加开销,恶意攻击会更严重。