要搞清楚这三种情况,首先先看看redis中请求是如何获取数据的。
当接到用户请求后,会先尝试重redis缓存中获取数据,如果缓存中能取到数据,则直接返回给用户。当缓存中不存在数据时,则从DB中获取数据,如果成功取到数据,则会去更新redis,再返回数据给用户。如图:
我们理解了以上概念后,来第一种情况,
缓存击穿:
定义:首先在高并发的情况下,某个热门key突然过期,导致大量的请求在redis中找不到数据,而直接去访问DB,使DB压力增大。(缓存击穿不会造成DB宕机,只会增大DB压力)
解决方案:
①redis中数据不设置过期时间,然后在缓存对象上添加过期时间,每次取数据时,校验该数据,如果属性要过期,则异步一个线程去更新缓存中的数据。(这里可能会拿到过期的值)
②设置热点key永不过期,然后添加一个互斥锁来保证缓存的单线程。
(互斥锁:对共享数据进行绑定,保证同一时刻只有一个线程操作)
缓存穿透:
定义:是指查询缓存和数据库中都不存在的数据,导致每次请求都直接访问数据库,形成缓存穿透。
解决方案:
①利用互斥锁,在缓存失效时,先去获得锁,得到了锁再去访问数据库,没得到则休眠一段时间重试。
②采用异步更新策略,无论key是否取到值,都直接返回。value中维护一个缓存失效时间,如果缓存过期,异步一个线程去读数据,更新缓存。(需要做缓存预热)
(缓存预热:项目启动前,先加载缓存)
③提供一个能迅速判断key值是否有效的拦截机制(比如:布隆过滤器)
④如果从数据库查询的对象为空,也放入缓存,把对象存活时间设置较短,比如60秒
缓存雪崩:
定义:大量缓存集中在同一时间内过期,出现大量的缓存击穿现象,所有请求都直接落在了DB上,引起DB压力过大,可能会造成宕机。
解决方案:
①给缓存的失效时间,加上随机值,避免集体失效。如果是redis集群部署,则热点信息均匀分布给不同的redis库中。
②使用互斥锁,但这样吞吐量会明显下降。
③设置热点信息永不过期。
④双缓存。缓存A,B,A设置失效时间为20min,B不设置失效时间,自己做预热操作。
具体细节:
1.从缓存A中读取数据,有则直接返回。
2.如果A没有数据,直接从B中读数据,直接返回,并启动一个更新线程(异步)。
3.更新线程同时更新缓存A和B。