Reids之缓存雪崩、缓存穿透、缓存击穿

1、缓存雪崩

缓存雪崩指的是原有的缓存数据出现了大批量的缓存过期,造成一时间大批量并发请求都到了数据库,造成数据库的压力激增,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

【解决方案】
从上面的分析可以看出只有当缓存同时大批量过期的时候,才会出现缓存雪崩的情形,所以只要想办法让缓存过期的时间分散开来即可。那方法就多了,比如说将热数据的缓存时间设置长一点,冷数据的过期时间设置得短一点,另外数据也可以分类设置不同的过期时间,或者加一个随机扰乱因子。

2、缓存穿透

缓存穿透是指查询一个不存在于缓存且不存在于数据库的数据,这时如果遭到恶意攻击,频繁地请求这个缺失数据,必然会每次都要读缓存和读数据库,增加了数据库的压力负担。

【解决方案】

  1. 对于缺失的数据,每次请求完数据库之后,都要更新到缓存,设置其key对应的value为空即可。这样当下一次请求该缺失数据对应的key,就可以直接通过缓存判断了,不需要再去请求数据库。
    ——缺点:显然,当数据缺失很多的时候,必然会极大地浪费缓存的内存空间。
  2. 采取bloom filter(布隆过滤器),布隆过滤器的原理如下。

3、布隆过滤器

首先明确我们要解决的问题,即如何快速判断一个数据是否存在于数据库中。那我们自然而然想到的便是采取hash思想,只需要将数据库中的所有数据key存储到一个hash结构中,便可以快速判断。但是这里有一个问题在于,一般我们面临的实际数据会很大,少则上亿的数据量,而且每一个数据key所占字节数都有可能会很大。我们假设数据量为109,每一个key的平均字节数为8byte,则存储这一部分key所需要的内存为 8*109byte ≈ 8GB,显然对于redis这种内存数据库而言,这样大的内存消耗是不可能承受的。所以我们提出了一个问题,是否存在这样一个数据结构,即能够快速查找,又能够尽可能的降低内存占用。

3.1 BitMap

https://www.jianshu.com/p/e530baada558
https://blog.csdn.net/kongmin_123/article/details/82257209
承接上面的问题,BitMap就是这样一个数据结构,即能够快速查找,又能够尽可能的降低内存占用。其原理在于,用一个bit位来表示一个key,无论这个key多大,都可以通过hash散列到具体某一个bit位上。这样每次查找该key时,都先通过一个hash函数进行映射,然后找到BitMap上对应bit位是0还是1,来判断其key是否存在。下面我们用一个具体的实例来说明。

场景一:20亿个整数中找出不重复的整数的个数,内存不足以容纳这20亿个整数。 (大数据查重)

  • 首先,根据“内存空间不足以容纳这05亿个整数”我们可以快速的联想到Bit-map。下边关键的问题就是怎么设计我们的Bit-map来表示这20亿个数字的状态了。其实这个问题很简单,一个数字的状态只有三种,分别为不存在,只有一个,有重复。因此,我们只需要2bits就可以对一个数字的状态进行存储了,假设我们设定一个数字不存在为00,存在一次01,存在两次及其以上为11。那我们大概需要存储空间2G左右。
  • 接下来的任务就是把这20亿个数字放进去(存储),如果对应的状态位为00,则将其变为01,表示存在一次;如果对应的状态位为01,则将其变为11,表示已经有一个了,即出现多次;如果为11,则对应的状态位保持不变,仍表示出现多次。
  • 最后,统计状态位为01的个数,就得到了不重复的数字个数,时间复杂度为O(n)。

场景二:对10亿个不重复的整数进行排序。(大数据排序)

  • 比如我要对{1,5,7,2}这四个byte类型的数字做排序,该怎么做呢?我们知道byte是占8个bit位,其实我们可以将数组中的值作为bit位的key,value用”0,1“来标识该key是否出现过?下面看图:
    在这里插入图片描述
    从图中我们精彩的看到,我们的数组值都已经作为byte中的key了,最后我只要遍历对应的bit位是否为1就可以了,那么自然就成有序数组了。

  • 可能有人说,我增加一个13怎么办?很简单,一个字节可以存放8个数,那我只要两个byte就可以解决问题了。
    在这里插入图片描述
    可以看出我将一个线性的数组变成了一个bit位的二维矩阵,最终我们需要的空间仅仅是:3.6G/32=0.1G即可

  • 要注意的是bitmap排序不是N的,而是取决于待排序数组中的最大值,在实际应用上关系也不大,比如我开10个线程去读byte数组,那么复杂度为:O(Max/10)。

虽然BitMap已经尽可能的压缩了空间了,但是对于内存数据库而言,其空间消耗依旧很大,1G的数据key就至少需要1G的空间,所以考虑能否进一步降低空间。

3.2 布隆过滤器

鉴于上述问题,我们发现在只有两种状态时,bitmap要求key和bit位一一对应,而且其空间大小受极值的影响,我们假设Redis的内存空间为2G,则它至多可以存储2G的key。但是如果我们让key和bit形成一对多的关系,且不受极值的影响,那它就可以存储更多的key。举个例子:我们假设用k=3个bit为来表示以个key,则2G的bit可以表示 C 2 G 3 C_{2G}^3 C2G3 个数,明显大于2G的空间。而布隆过滤器正是采取这样的思想,通过多个hash函数对一个key进行散列,这样一个key就可以对应多个bit位,从而进一步降低了空间。

所以Bitmap的好处在于空间复杂度不随原始集合内元素的个数增加而增加,而它的坏处也源于这一点——空间复杂度随集合内最大元素增大而线性增大。

所以接下来,我们要引入另一个著名的工业实现——布隆过滤器(Bloom Filter)。如果说Bitmap对于每一个可能的整型值,通过直接寻址的方式进行映射,相当于使用了一个哈希函数,那布隆过滤器就是引入了k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。下图中是k=3时的布隆过滤器。
在这里插入图片描述

但是我们也发现了一个问题,布隆过滤器,可能存在误报的可能,比如不同的key由于散列的bit位存在重叠的可能,所以有可能导致,某个key虽然不在数据库中,但是其对应的bit位却全是1,这样就造成了误报。但是布隆过滤器可以保证,如果某个key所对应的key不全为1,它必然不存在于数据库中。

参考链接:https://blog.csdn.net/zdxiq000/article/details/57626464

布隆过滤器通过引入一定错误率,使得海量数据判重在可以接受的内存代价中得以实现。从上面的公式可以看出,随着集合中的元素不断输入过滤器中(nnn增大),误差将越来越大。但是,当Bitmap的大小mmm(指bit数)足够大时,比如比所有可能出现的不重复元素个数还要大10倍以上时,错误概率是可以接受的。相比于单纯的bitmap,这个算法跳出了空间复杂度对待判元素值域的依赖,转而依赖总元素个数,这是一个更加工程可实现的算法——前者不管你的数集有多大,所需要的内存空间是一定的;后者,数集越大,想要达到相同误判率,所需要的内存空间就越大。

4、缓存击穿

缓存击穿指的是,某个热点数据过期了,然后此时高并发请求,在查询数据库然后回写到redis的期间,全都去访问数据库了,这样就极有可能造成服务器宕机。
解决方案:

  • 直接对接口限流,不推荐
  • 设置热点数据永不过期
  • 增加分布式锁,对于要访问数据库的热点数据的操作时,必须先加分布式锁,才能访问,这样就保证了高并发请求只会有一个到达服务器db,其他没拿到锁的请求,会一直尝试从redis中获取数据。
static Lock reenLock = new ReentrantLock();
public List<String> getData04() throws InterruptedException {
    List<String> result = new ArrayList<String>();
    // 从缓存读取数据
    result = getDataFromCache();
    if (result.isEmpty()) {
        if (reenLock.tryLock()) {
            try {
                System.out.println("我拿到锁了,从DB获取数据库后写入缓存");
                // 从数据库查询数据
                result = getDataFromDB();
                // 将查询到的数据写入缓存
                setDataToCache(result);
            } finally {
                reenLock.unlock();// 释放锁
            }
        } else {
            result = getDataFromCache();// 先查一下缓存
            if (result.isEmpty()) {
                System.out.println("我没拿到锁,缓存也没数据,先小憩一下");
                Thread.sleep(100);// 小憩一会儿
                return getData04();// 重试
            }
        }
    }
    return result;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值