缓存击穿
假设一个缓存系统中存在一个热Key,每分每秒都有大量的请求访问这个key,那么当这个热key因为过期而失效,一瞬间所有的请求直接打到DB上,这种场景称做缓存击穿。
为了避免这种问题业界一般有如下解决方案
1.互斥锁
当缓存过期时,常见的逻辑是直接请求DB,然后再set回缓存中。为了避免缓存击穿的问题,可以在请求DB的地方加一个锁(如果是分布式系统就需要使用分布式锁),争取到锁的就去访问DB,没争取到的就阻塞一段时间再请求缓存,减少直接打到DB的请求。
2.永不过期
不给key设置有效期,让key永不过期,然后以定时任务的形式,主动去更新缓存。
3.逻辑过期
同样不给key设置有效期,而是将expired值作为缓存数据的一个字段放入缓存中,取出来时校验一下是否过期,如果过期了则重新设置有效期,并起一个异步线程去更新这缓存。
缓存雪崩
假设一个缓存系统中,大量的key用的都是同一个expired值,那么在某一瞬间,这些key可能会集体全部失效,所有的请求直接打到DB上,这种场景称作缓存雪崩。
如何解决?
1.随机过期时间
将数据写入缓存时往往会设置一个expired值,为了避免在某一时间统统全部过期,可以在expired值的基础上加上1~5min的随机值,避免这种情况。这样缓存就不会在某一瞬间突然全部过期了。
缓存穿透
当用户访问一个不存在的key时,缓存取不到,按逻辑就是从库里面取,这种请求一多,打到DB的请求也就增加了,这种场景称作缓存击穿。
如下,用户不断请求用户信息,但ID都为无效的负数,缓存里面没有,所有请求可能直接打到DB上,如果是恶意请求的话,危害颇大。
GET /user/-1 GET /user/-2 .... GET /user/-3
1.缓存不存在的值
如果查询到一个空的结果时,可以仍然将这个空的结果进行缓存,下次在请求的话就会这就返回这个空的结果而不是请求DB。当然,如果这种请求一多,存储里就会存储大量这种无效值,对缓存的空间也是一种压力,所以推荐使用下面BloomFilter的方式。
2.BloomFilter
BloomFilter的特性就是当BloomFilter认为有数据时,是可能有,当认为没有数据时,那就是真没有。故可以利用BloomFilter的特性,挡掉这些不存在的请求。
缓存预热
系统刚启动,缓存还未完全构建,但是用户不会管这么多,直接一股脑的访问过来,请求又直接打到DB了。如果遇到这种场景,可以利用缓存预热的思想,在系统启动时调用一个后台接口,构建一遍缓存。