ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

缓存雪崩和缓存穿透

2021-05-22 17:33:42  阅读:157  来源: 互联网

标签:缓存 redis 布隆 穿透 static 雪崩 过滤器 import


缓存雪崩

发生:
redis主机挂了,Redis 全盘崩溃
比如缓存中有大量数据同时过期

解决:
redis缓存集群实现高可用,主从+哨兵
Redis Cluster
ehcache本地缓存 + Hystrix或者阿里sentinel限流&降级
开启Redis持久化机制aof/rdb,尽快恢复缓存集群

缓存穿透:

请求去查询一条记录,先redis后mysql发现都查询不到该条记录,
但是请求每次都会打到数据库上面去,导致后台数据库压力暴增,
这种现象我们称为缓存穿透,这个redis变成了一个摆设。。。。。。
简单说就是本来无一物,既不在Redis缓存中,也不在数据库中

危害:
第一次来查询后,一般我们有回写redis机制
第二次来查的时候redis就有了,偶尔出现穿透现象一般情况无关紧要

解决:
方案1:空对象缓存或者缺省值
在这里插入图片描述
黑客或者恶意攻击:
黑客会对你的系统进行攻击,拿一个不存在的id 去查询数据,会产生大量的请求到数据库去查询。可能会导致你的数据库由于压力过大而宕掉

id相同打你系统:
第一次打到mysql,空对象缓存后第二次就返回null了,避免mysql被攻击,不用再到数据库中去走一圈了

id不同打你系统:
由于存在空对象缓存和缓存回写(看自己业务不限死),redis中的无关紧要的key也会越写越多**(记得设置redis过期时间)**

方案2:Google布隆过滤器Guava解决缓存穿透

	Guava 中布隆过滤器的实现算是比较权威的,所以实际项目中我们不需要手动实现一个布隆过滤器

Guava’s BloomFilter 源码剖析:
https://github.com/google/guava/blob/master/guava/src/com/google/common/hash/BloomFilter.java

 <!--guava Google 开源的 Guava 中自带的布隆过滤器-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

helloworld案例:


    @Test
    public void bloomFilter()
    {
// 创建布隆过滤器对象
        BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 100);
// 判断指定元素是否存在
        System.out.println(filter.mightContain(1));
        System.out.println(filter.mightContain(2));
// 将元素添加进布隆过滤器
        filter.put(1);
        filter.put(2);
        System.out.println(filter.mightContain(1));
        System.out.println(filter.mightContain(2));
    }

新建测试类类BloomfilterDemo
取样本100W数据,查查不在100W范围内的其它10W数据是否存在

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import java.util.ArrayList;
import java.util.List;

public class BloomfilterDemo
{
    public static final int _1W = 10000;

    //布隆过滤器里预计要插入多少数据
    public static int size = 100 * _1W;
    //误判率,它越小误判的个数也就越少(思考,是不是可以设置的无限小,没有误判岂不更好)
    public static double fpp = 0.03;

    // 构建布隆过滤器
    private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size,fpp);


    public static void main(String[] args)
    {
        //1 先往布隆过滤器里面插入100万的样本数据
        for (int i = 0; i < size; i++) {
            bloomFilter.put(i);
        }

        //故意取10万个不在过滤器里的值,看看有多少个会被认为在过滤器里
        List<Integer> list = new ArrayList<>(10 * _1W);

        for (int i = size+1; i < size + 100000; i++) {
            if (bloomFilter.mightContain(i)) {
                System.out.println(i+"\t"+"被误判了.");
                list.add(i);
            }
        }
        System.out.println("误判的数量:" + list.size());

    }

}
 

上一步结论
现在总共有10万数据是不存在的,误判了3033次,
原始样本:100W
不存在数据:101W—110W
在这里插入图片描述
debug源码分析下,看看hash函数

布隆过滤器说明:
在这里插入图片描述
方案3:Redis布隆过滤器解决缓存穿透

Guava缺点说明
Guava 提供的布隆过滤器的实现还是很不错的 (想要详细了解的可以看一下它的源码实现),但是它有一个重大的缺陷就是只能单机使用 ,而现在互联网一般都是分布式的场景。

为了解决这个问题,我们就需要用到 Redis 中的布隆过滤器了

案例:白名单过滤器:
白名单架构说明:
在这里插入图片描述
误判问题,但是概率小可以接受,不能从布隆过滤器删除
全部合法的key都需要放入过滤器+redis里面,不然数据就是返回null

code:

import org.redisson.Redisson;
import org.redisson.RedissonBloomFilter;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;

public class RedissonBloomFilterDemo2
{
    public static final int _1W = 10000;

    //布隆过滤器里预计要插入多少数据
    public static int size = 100 * _1W;
    //误判率,它越小误判的个数也就越少
    public static double fpp = 0.03;

    static RedissonClient redissonClient = null;
    static RBloomFilter rBloomFilter = null;

    static
    {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.111.147:6379").setDatabase(0);
        //构造redisson
        redissonClient = Redisson.create(config);
        //通过redisson构造rBloomFilter
        rBloomFilter = redissonClient.getBloomFilter("phoneListBloomFilter",new StringCodec());

        rBloomFilter.tryInit(size,fpp);

        // 1测试  布隆过滤器有+redis有
        rBloomFilter.add("10086");
        redissonClient.getBucket("10086",new StringCodec()).set("chinamobile10086");

        // 2测试  布隆过滤器有+redis无
        //rBloomFilter.add("10087");

        //3 测试 ,都没有

    }

    public static void main(String[] args)
    {
        String phoneListById = getPhoneListById("10087");
        System.out.println("------查询出来的结果: "+phoneListById);

        //暂停几秒钟线程
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
        redissonClient.shutdown();
    }

    private static String getPhoneListById(String IDNumber)
    {
        String result = null;

        if (IDNumber == null) {
            return null;
        }
        //1 先去布隆过滤器里面查询
        if (rBloomFilter.contains(IDNumber)) {
            //2 布隆过滤器里有,再去redis里面查询
            RBucket<String> rBucket = redissonClient.getBucket(IDNumber, new StringCodec());
            result = rBucket.get();
            if(result != null)
            {
                return "i come from redis: "+result;
            }else{
                result = getPhoneListByMySQL(IDNumber);
                if (result == null) {
                    return null;
                }
                // 重新将数据更新回redis
                redissonClient.getBucket(IDNumber,new StringCodec()).set(result);
            }
            return "i come from mysql: "+result;
        }
        return result;
    }

    private static String getPhoneListByMySQL(String IDNumber)
    {
        return "chinamobile"+IDNumber;
    }

}

重要总结:
在这里插入图片描述
黑名单使用
在这里插入图片描述
在centos7下布隆过滤器2种安装方式:
采用docker安装RedisBloom,推荐:
Redis 在 4.0 之后有了插件功能(Module),可以使用外部的扩展功能,
可以使用 RedisBloom 作为 Redis 布隆过滤器插件。

docker run -p 6379:6379 --name=redis6379bloom -d redislabs/rebloom

在这里插入图片描述
docker exec -it redis6379bloom /bin/bash redis-cli

布隆过滤器常用操作命令:

在这里插入图片描述
bf.reserve filter 0.01 100
bf.add filter v11
bf.exists filter v11
bf.exists filter v12

bf.reserve key error_rate的值 initial_size 的值
默认的error_rate是 0.01,
默认的initial_size是 100。

bf.add key 值
bf.exists key 值
bf.madd 一次添加多个元素
bf.mexists 一次查询多个元素是否存在

编译安装:


# 下载 编译 安装Rebloom插件
wget https://github.com/RedisLabsModules/rebloom/archive/v2.2.2.tar.gz
# 解压 
tar -zxvf v2.2.2.tar.gz
cd RedisBloom-2.2.2
# 若是第一次使用 需要安装gcc++环境
make
# redis服启动添加对应参数 这样写还是挺麻烦的
# rebloom_module="/usr/local/rebloom/rebloom.so"
# daemon --user ${REDIS_USER-redis} "$exec $REDIS_CONFIG --loadmodule # $rebloom_module --daemonize yes --pidfile $pidfile"
# 记录当前位置
pwd
# 进入reids目录 配置在redis.conf中 更加方便
vim redis.conf
# :/loadmodule redisbloom.so是刚才具体的pwd位置 cv一下
loadmodule /xxx/redis/redis-5.0.8/RedisBloom-2.2.2/redisbloom.so
# 保存退出
wq
# 重新启动redis-server 我是在redis中 操作的 若不在请写出具体位置 不然会报错
redis-server redis.conf
# 连接容器中的 redis 服务 若是无密码 redis-cli即可
redis-cli -a 密码
# 进入可以使用BF.ADD命令算成功

在这里插入图片描述

标签:缓存,redis,布隆,穿透,static,雪崩,过滤器,import
来源: https://blog.csdn.net/zhumengguang/article/details/117165996

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有