Redis缓存穿透、缓存雪崩的分析及解决方案

什么是缓存击穿

缓存击穿是查询数据库中不存在的数据,如果有用户恶意模拟请求很多缓存中不存在的数据,由于缓存中都没有,导致这些请求短时间内直接落在了DB上,对DB产生压力,导致数据库异常。

解决方案

布隆过滤器

最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小。
maven工程引入guava包:

<dependencies>  
     <dependency>  
         <groupId>com.google.guava</groupId>  
         <artifactId>guava</artifactId>  
         <version>23.0</version>  
     </dependency>  
</dependencies>  

redis实现代码

    public String getByKey(String key) {
        // 通过key获取value
        String value = redis.get(key);
        if (StringUtil.isEmpty(value)) {
            if (bloomFilter.mightContain(key)) {
                value = xxxService.get(key);
                redis.set(key, value);
                return value;
            } else {
                return null;
            }
        }
        return value;
    }

布隆过滤器判断一个数是否存在于一个百万级别的集合中,只要0.2ms就可以完成,性能极佳。BloomFilter的默认的容错率是0.03。
要注意的是:误判率越低,底层维护的数组越长,占用空间越大。因此,误判率实际取值,根据服务器所能够承受的负载来决定;布隆过滤器不支持删除操作。

使用互斥锁

该方法是比较普遍的做法,在根据key获得的value值为空时加锁,再从数据库加载,加载完成后释放锁。若其他线程获取锁失败时睡眠一段时间后重试。
单机环境用并发包的普通锁(synchronized、Lock)类型就行,集群环境则使用分布式锁(Redis的setnx)
集群环境Redis分布式锁实现如下

public String get(key) {
    String value = redis.get(key);
    if (value == null) { //代表缓存值过期
        //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
        if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功
            value = db.get(key);
            redis.set(key, value, expire_secs);
            redis.del(key_mutex);
        } else {
            //其他线程休息50毫秒后重试
            sleep(50);
            get(key);
        }
    } else {
        return value;        
    }
}

将空值放入缓存

如果查询数据库也为空,也设置一个默认空值存放到缓存,并设置一定的失效时间(不超过五分钟),这样第二次到缓存中获取就有返回值,而不会继续访问数据库,这种办法最简单粗暴。

缓存空对象会有两个问题:

第一,空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间 ( 如果是攻击,问题更严重 ),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。

第二,缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为 5分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式清除掉缓存层中的空对象。

什么是缓存失效

缓存雪崩是指在我们设置缓存时采用了相同的过期时间,缓存在同一时间内大量键过期,此时有多个进程就请求会全部转发到DB,导致数据库中导致连接异常。

解决方案

使用互斥锁

与解决缓存击穿问题一样用加锁排队,实现同上。

在失效上加随机值

还有一个简单方案就是将缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,避免引发集体失效。


上一篇
下一篇
微服务架构下的分布式事务解决方案 微服务架构下的分布式事务解决方案
1 微服务的发展微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇,很多互联网行业巨头、开源社区等都开始了微服务的讨论和实践。Hailo有160个不同服务
2018-11-17