【面试题】redis雪崩、击穿、穿透(包含布隆过滤器原理分析)

redis

  • 1. Redis雪崩
    • 1.1 解决方案
  • 2. 缓存穿透
    • 2.1 解决方案
  • 3.0 缓存击穿
    • 3.1 解决方案
  • 4. 扩展 - 布隆过滤器
    • 4.1 布隆过滤器是什么?(判断某个key一定不存在)
    • 4.2 使用
    • 4.3 应用场景
    • 4.4 底层原理
      • 4.4.1 添加值到布隆过滤器
      • 4.4.2 查询
      • 4.4.3 删除:不支持
    • 4.5 原理小demo

1. Redis雪崩

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

1.1 解决方案

处理缓存雪崩简单,在批量往Redis存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会在同一时间大面积失效,我相信,Redis这点流量还是顶得住的。
setRedis(Key,value,time + Math.random() * 10000);
或者设置热点数据永远不过期,有更新操作就更新缓存就好了(比如运维更新了首页商品,那你刷下缓存就完事了,不要设置过期时间),电商首页的数据也可以用这个操作,保险。

2. 缓存穿透

指缓存和数据库中都没有的数据,而用户不断发起请求,我们数据库的 id 都是1开始自增上去的,如发起为id值为 -1 的数据或 id 为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重会击垮数据库。
举个简单的例子,你这个接口是分页查询的,但是你没对分页参数的大小做限制,调用的人万一一口气查 Integer.MAX_VALUE 一次请求就要你几秒,多几个并发你不就挂了么?

2.1 解决方案

1.在接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码Return,比如:id 做基础校验,id <=0的直接拦截等。
2.从缓存取不到的数据,在数据库中也没有取到,这时也可以将对应Key的Value对写为null、位置错误、稍后重试这样的值具体取啥问产品.
3.还有我记得Redis还有一个高级用法布隆过滤器(Bloom Filter)这个也能很好的防止缓存穿透的发生,他的原理也很简单就是利用高效的数据结构和算法快速判断出你这个Key是否在数据库中存在,不存在你return就好了,存在你就去查了DB刷新KV再return。

3.0 缓存击穿

这个跟缓存雪崩有点像,但是又有一点不一样,缓存雪崩是因为大面积的缓存失效,打崩了DB,而缓存击穿不同的是缓存击穿是指一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。

3.1 解决方案

设置热点数据永远不过期。或者加上互斥锁就能搞定了
【面试题】redis雪崩、击穿、穿透(包含布隆过滤器原理分析)_第1张图片

一般避免以上情况发生我们从三个时间段去分析下:

事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
事中:本地 ehcache 缓存 + Hystrix 限流+降级,避免MySQL 被打死。
事后:Redis 持久化 RDB+AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

4. 扩展 - 布隆过滤器

4.1 布隆过滤器是什么?(判断某个key一定不存在)

  • 特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。
  • 相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。

4.2 使用

当用户来查询某一个row时,可以先通过内存中的布隆过滤器过滤掉大量不存在的row请求,然后去再磁盘进行查询。
布隆过滤器说某个值不存在时,那肯定就是不存在,可以显著降低数据库IO请求数量

4.3 应用场景

1)场景1(给用户推荐新闻)
当用户看过的新闻,肯定会被过滤掉,对于没有看过的新闻,可能会过滤极少的一部分(误判)。
这样可以完全保证推送给用户的新闻都是无重复的。

2)场景2(爬虫url去重)
在爬虫系统中,我们需要对url去重,已经爬取的页面不再爬取
当url高达几千万时,如果一个集合去装下这些URL地址非常浪费空间
使用布隆过滤器可以大幅降低去重存储消耗,只不过也会使爬虫系统错过少量页面

4.4 底层原理

布隆过滤器本质是一个位数组,位数组就是数组的每个元素都只占用 1 bit 。每个元素只能是 0 或者 1。这样申请一个 10000 个元素的位数组只占用 10000 / 8 = 1250 B 的空间。

4.4.1 添加值到布隆过滤器

  1. 向布隆过滤器添加key,会使用 f、g、h hash函数对key算出一个整数索引,然后对长度取余
  2. 每个hash函数都会算出一个不同的位置,把算出的位置都设置成1就完成了布隆过滤器添加过程

4.4.2 查询

  1. 当查询某个key时,先用hash函数算出一个整数索引,然后对长度取余
  2. 当你有一个不为1时肯定不存在这个key,当全部都为1时可能有这个key
  3. 这样内存中的布隆过滤器过滤掉大量不存在的row请求,然后去再磁盘进行查询,减少IO操作

4.4.3 删除:不支持

目前我们知道布隆过滤器可以支持 add 和 isExist 操作

*如果都是 1,这并不能说明这个 key 就一定存在,只是极有可能存在,因为这些位被置为 1 可能是因为其它的 key 存在所致。如果这个位数组比较稀疏,判断正确的概率就会很大,如果这个位数组比较拥挤,判断正确的概率就会降低。
【面试题】redis雪崩、击穿、穿透(包含布隆过滤器原理分析)_第2张图片

4.5 原理小demo

package com.jd.demo.test;

import java.util.Arrays;
import java.util.BitSet;
import java.util.concurrent.atomic.AtomicBoolean;

public class MyBloomFilter {
    //你的布隆过滤器容量
    private static final int DEFAULT_SIZE = 2 << 28;
    //bit数组,用来存放结果
    private static BitSet bitSet = new BitSet(DEFAULT_SIZE);
    //后面hash函数会用到,用来生成不同的hash值,可随意设置,别问我为什么这么多8,图个吉利
    private static final int[] hashInts = {1, 6, 16, 38, 58, 68};

    //add方法,计算出key的hash值,并将对应下标置为true
    public void add(Object key) {
        Arrays.stream(hashInts ).forEach(i -> bitSet.set(hash(key, i)));
    }

    //判断key是否存在,true不一定说明key存在,但是false一定说明不存在
    public boolean isContain(Object key) {
         boolean result = true;
        for (int i : hashInts) {
        	//短路与,只要有一个bit位为false,则返回false
            result = result && bitSet.get(hash(key, i));
        }
        return result;
    }

    //hash函数,借鉴了hashmap的扰动算法,强烈建议大家把这个hash算法看懂,这个设计真的牛皮加闪电
    private int hash(Object key, int i) {
        int h;
        return key == null ? 0 : (i * (DEFAULT_SIZE - 1) & ((h = key.hashCode()) ^ (h >>> 16)));
    }
}

来源:https://blog.csdn.net/qq_35190492/article/details/102889333
demo来源:https://blog.csdn.net/qq_33709582/article/details/108407706
来源:https://blog.csdn.net/u013030276/article/details/88381868?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.baidujs&dist_request_id=1328592.25069.16148471634457959&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.baidujs

你可能感兴趣的:(面试题-,redis,面试)