Redis缓存击穿,缓存穿透,缓存雪崩的解决方案

缓存击穿,缓存穿透,缓存雪崩,是常见的缓存使用不当导致的问题,今天我们一起梳理梳理。

目录

一、什么是缓存击穿?

二、什么是缓存穿透

三、什么是缓存雪崩


一、什么是缓存击穿?

缓存穿透问题非常好理解,redis中热点数据key如果过期,请求会导致大量的请求直接落在数据库上,这样数据库就会被直接打死。就像缓存这道屏障在key处被击穿一样,这就是缓存击穿。

怎么预防缓存击穿问题呢?

1.防止缓存数据失效,比如设置热点数据key有效期永久,不过期。

这样缓存数据就不会失效,而导致请求全部落在数据库上。个人认为该方案虽然简单,但是不利于缓存的管理,因为key不一定一直是热点数据,不同的时间和场合,热点数据会不一样,盲目的设置不过期,会导致缓存中不过期的数据越来越多,消耗内存。

2.采用分布式锁

采用分布式锁方案,只允许一个线程去访问数据库并更新缓存,其他线程会等待直到缓存中读取到数据。

方案一:同步更新缓存

利用setnx实现分布式锁。

String get(String key){
    String value = redis.get(key);
    if(value == null){
        String key_mutex = "keymutex";
        if(redis.setnx(key_mutex,"1")){
            redis.expire(key_mutex,3*60);
            value = db.get(key);
            redis.set(key,value);
            redis.delete(key_mutex);
        }esle{
            //其他线程休息等待50毫秒后重新访问函数
            Thread.Sleep(50);
            value = redis.get(key);
        }
    }
    return value;
}

上述代码简单,优点是代码简单和能保证一致性,缺点有两个,第一户需要等待。第二,若线程在执行redis.delete(key_mutex);时掉线,导致锁未释放,会导致死锁问题,setnx又不具备设置过期时间的功能,因此需要在value上设置“过期时间”。(后续在讨论这个问题)。

方案二:异步更新缓存

String get(String key){
    String value = redis.get(key);
    if(value == null){
        threadPool.execute(new Runnable(){
            public void run(){
                String key_mutex = "keymutex";
                if(redis.setnx(key_mutex,"1")){
                    redis.expire(key_mutex,3*60);
                    String dbValue = db.get(key);
                    redis.set(key,dbValue);
                    redis.delete(key_mutex);
                }
            }
        });
    }
    return value;
}

上述代码,利用一个线程去更新缓存,优点是,用户可以不用等待快速得到结果。但是缺点很明显,这样会导致数据不一致性,用户得到的结果可能不是最新的。

二、什么是缓存穿透

在某个时间段,出现大量数据请求,而且这些请求的数据,既不在缓存中,又不在数据库中,这样的结果就是数据库被打死。导致缓存穿透的原因,可能是系统设计不合理,导致大量非法的用户请求;也有可能是刻意的人为攻击,利用不存在的数据进行请求。

怎么预防缓存击穿问题呢?

1、增加用户验证和参数校验,防止非法用户发起攻击利用非法的数据发动攻击;

2、将类似的数据请求(既不在缓存中,又不在数据库中的数据),存在缓存中设置过期时间防止同一非法数据反复访问数据库。

三、什么是缓存雪崩

缓存雪崩是指请求未在缓存中找到请求的数据(这些数据曾经在缓存中),导致短时间内大量请求落在数据库上,从而打死数据库。造成缓存雪崩的原因,可能是缓存大面积的到期失效,也可能是缓存宕机。

怎么预防缓存雪崩问题呢?

1、如果是因为缓存宕机,则应该是用redis的哨兵模式,实现缓存的高可用性。(后续在学习redis的三种部署模式)

2、如果是缓存大表面积失效

         2.1、将key的过期时间按一定方式随机设置,使得缓存不至于大面积同时失效。

         2.2、访问到失效的key时的恢复方案。从这个层面上讲,缓存雪崩和缓存击穿有相似的地方,可以按照缓存击穿的方案来做。但是不同的是,缓存击穿是指若干个热点数据失效,导致数据库被打死,特点是,请求的数据大部分都是相同的,因此只用将这几个失效的热点数据加锁回复就能解决。而缓存雪崩,失效的可能不只是若干个热点数据,而是大面积的数据失效,热点数据只是包含在其中,缓存击穿的方案只能解决相同的请求大量访问数据库的问题,而当面对大量不相同的请求时,该方案的会起到一定的效果,但是效果可能不大(更具实际情况而定)。因此这里除了实行预防缓存击穿的方案外,甚至还要进行限流降级。比如只允许三分之一的请求通过,这样对于用户而言只是多点击几次按钮(平均三次)就能请求到数据,这远比数据库被打死,直接访问崩溃带来的影响小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值