数据雪崩和数据穿透、击穿

目录

1、数据雪崩

2、数据穿透

3、数据击穿


1、数据雪崩

  • 问题

定时刷新清理缓存中的key时,Redis缓存中大面积key失效,从而导致大量请求直接访问了数据库,导致数据库崩盘。

  • 举个栗子

目前电商首页以及热点数据都会去做缓存,一般缓存都是定时任务去刷新,或者查不到之后去更新缓存的,定时任务刷新就有一个问题。

如果首页所有 Key 的失效时间都是 12 小时,中午 12 点刷新的,我零点有个大促活动大量用户涌入,假设每秒 6000 个请求,本来缓存可以抗住每秒 5000 个请求,但是缓存中所有 Key 都失效了。此时 6000 个/秒的请求全部落在了数据库上,数据库必然扛不住,真实情况可能 DBA 都没反应过来直接挂了。此时,如果没什么特别的方案来处理,DBA 很着急,重启数据库,但是数据库立马又被新流量给打死了。这就是我理解的缓存雪崩。

同一时间大面积失效,瞬间 Redis 跟没有一样,那这个数量级别的请求直接打到数据库几乎是灾难性的。你想想如果挂的是一个用户服务的库,那其他依赖他的库所有接口几乎都会报错。如果没做熔断等策略基本上就是瞬间挂一片的节奏,你怎么重启用户都会把你打挂,等你重启好的时候,用户早睡觉去了,临睡之前,骂骂咧咧“什么垃圾产品”。

  • 解决

对症下药,避免缓存中出现大量key同时失效的情况。

具体来说:

1)设置失效时间。批量往 Redis 存数据的时候,把每个 Key 的失效时间都加个随机值就好了,这样可以保证数据不会再同一时间大面积失效;

setRedis(key, value, time+Math.random()*10000);

2)热点数据均匀分布如果 Redis 是集群部署,将热点数据均匀分布在不同的 Redis 库中也能避免全部失效。

3)取消设置热点数据有一个失效时间,用更新缓存代替。或者设置热点数据永不过期,有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷下缓存就好了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。


2、数据穿透

  • 问题

缓存穿透:缓存和数据库中都没有的数据,而用户(黑客)不断发起请求。

  • 例子

我们数据库的 id 都是从 1 自增的,如果发起 id=-1 的数据或者 id 特别大不存在的数据,这样的不断攻击导致数据库压力很大,严重会击垮数据库。

  • 解决

1)增加校验。比如用户鉴权,参数做校验,不合法的校验直接 return,比如 id 做基础校验,id<=0 直接拦截;

2)布隆过滤器。Redis 里还有一个高级用法布隆过滤器(Bloom Filter)这个也能很好的预防缓存穿透的发生。

它的原理也很简单,就是利用高效的数据结构和算法快速判断出你这个 Key 是否在数据库中存在,不存在你 return 就好了,存在你就去查 DB 刷新 KV 再 return。

 


3、数据击穿

  • 问题

缓存击穿嘛,这个跟缓存雪崩有点像,但是又有一点不一样,缓存雪崩是因为大面积的缓存失效,打崩了 DB。

而缓存击穿不同的是缓存击穿是指一个 Key 非常热点,在不停地扛着大量的请求,大并发集中对这一个点进行访问,当这个 Key 在失效的瞬间,持续的大并发直接落到了数据库上,就在这个 Key 的点上击穿了缓存。

  • 解决

1)设置热点数据永不过期;

2)或者加上互斥锁就搞定了

public static String getData(String key) throws InterruptedException {
        //从Redis查询数据 
        String result = getDataByKV(key);
        //参数校验
        if (StringUtils.isBlank(result)) {
            try {//获得锁
                if (reenLock.tryLock()) {
                    //去数据库查询 
                    result = getDataByDB(key);
                    //校验
                    if (StringUtils.isNotBlank(result)) {
                        //插进缓存 
                        setDataToKV(key, result);
                    }
                } else {
                    //睡一会再拿
                    Thread.sleep(100L);
                    result = getData(key);
                }
            } finally {
                //释放锁 
                reenLock.unlock();
            }
        }
        return result;
    }

 

你可能感兴趣的:(Redis,面经)