缓存穿透

1.应用服务器压力变大

2.redis命中率降低了

3.一直查询数据库

key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

出现原因

1.redis查询不到数据库

2.出现很多非正常url访问

解决方案

1.对空值进行缓存,过期时间很短

2.设置可访问白名单,用bitmap定义访问名单,效率可能会低

3.采用布隆过滤器,命中率无保障

4.实时监控,发现命中率降低,则排查访问对象,设置黑名单

例子

//伪代码
public object GetProductListNew() {
    int cacheTime = 30;
    String cacheKey = "product_list";

    String cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) {
        return cacheValue;
    }

    cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) {
        return cacheValue;
    } else {
        //数据库查询不到,为空
        cacheValue = GetProductListFromDB();
        if (cacheValue == null) {
            //如果发现为空,设置个默认值,也缓存起来
            cacheValue = string.Empty;
        }
        CacheHelper.Add(cacheKey, cacheValue, cacheTime);
        return cacheValue;
    }
}

缓存击穿

说缓存击穿之前,我们先来了解一个概念——热点key,某个访问非常频繁,访问量非常大的一个缓存key,我们叫做热点key。

缓存击穿是指某个热点key在失效的瞬间(一般是缓存时间到期),持续的大并发请求穿破缓存,直接打到数据库,就像在一个屏障上凿开了一个洞,造成数据库压力瞬间增大,这就是缓存击穿。

现象:

1.数据库访问压力突然变大

2.redis并没有出现大量key过期

3.redis正常运行

解决方案

1.预先设置热门数据:在高峰访问的时候,把一些热门数据提前射入,加大时长

2.实施调控:现场监控,实时调整过期时长

缓存击穿的解决方案
1、设置热点key永不过期

2、加锁,根据热点key从缓存中获取到的value为空时,先锁上,再去查DB将数据加载到缓存,若其它线程获取锁失败,则等待一段时间后重试,从而避免了大量请求直接打到DB。单机可以使用synchronized或ReentrantLock,分布式需要加分布式锁,如Redis分布式锁。【为了不阻塞对其他key的请求,此处可以用热点key来加锁】

缓存雪崩

(大量key过期)缓存雪崩是指缓存由于某些原因整体或者大量失效,导致大量请求打到后端数据库,从而导致数据库崩溃,整个系统崩溃,发生灾难。

导致缓存整体或大量失效的场景一般有:

1、缓存服务宕机,如Redis集群彻底崩溃;

2、在某个集中的时间段内,系统预加载的缓存集中失效了;

解决方法

1.使用多级缓存架构

2.使用锁或队列(不适用高并发)

3.设置过期标志更新缓存:提前设置变量记录是否过期,如果过期通知另外的线程取后台更新实际key的缓存

4.将缓存失效时间分散开