Redis的缓存穿透、缓存击穿和缓存雪崩及其解决方案
缓存穿透(查询不存在的数据)
用户查询一个数据,发现缓存中没有(key不存在或者key已经过期),也就是缓存没有命中。于是去数据库中查询,发现也没有(如果有,会将查询到的对象放进缓存,则不会发生缓存穿透)。当用户很多的时候,缓存都没有命中,于是都去请求了数据库,给数据库造成很大的压力。
解决方案:
1)设置拦截器
将所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。
2)布隆过滤器
将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
3)缓存空对象
将key-value写为key-null,设置较短过期时间,如30秒(设置太长会导致正常情况也没法使用)。
缓存击穿(热点key过期)
某个热点key,有大量的请求并发访问,当这个key在失效的瞬间,持续的高并发请求直接访问数据库,并回写缓存,导致数据库瞬间压力过大。
解决方案:
1)设置热点数据永不过期
2)加互斥锁
使用分布式锁,保证对于每个 key 同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因对分布式锁的考验很大。
缓存雪崩(缓存集中过期)
大量的key设置了相同的过期时间,导致缓存在同一时刻失效,造成瞬时数据库请求量大、压力骤增,引起雪崩。
解决方案:
1)随机设置key失效时间,避免大量key集体失效
2)设置热点数据永不过期
3)Redis集群
将热点数据均匀分布在不同的Redis节点,避免key全部失效。
4)限流降级
在缓存失效后,通过加锁或者队列来控制数据库写缓存的线程数量。比如:对某个 key只允许一个线程查询数据和写缓存,其他线程等待。
5)数据预热
在正式部署之前,把可能的数据线预先访问一遍,这样部分可能大量访问的数据就会加载到缓存。