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

6 篇文章 0 订阅
5 篇文章 0 订阅

 

1.缓存穿透


 1.1.什么是缓存穿透 正常情况下我们去查数据都是存在的 那么请求一条压根不存在的数据也就是缓存和DB都没有的数据那么请求就都会落在DB上面了 这种查询不存在的数据成为缓存穿透
 1.2.缓存穿透带来的问题,如果有黑客攻击无限请求一个不存在的数据那么请求的压力都会落在DB,那么DB就有可能宕机
 1.3.缓存穿透解决方案
  1.3.1之所以是因为发生缓存穿透是因为获取的key在缓存中不存在,那么只要在缓存中设置key为null就可以 但是别忘了设置过期时间
  1.3.2如果获取的不存在key比较多,就没办法一个一个放在key中,那么可以通过bloomFilter(布隆过滤),这种方式在大数据场景应用比较多,比如 Hbase 中使用它去判断数据是否在磁盘上。还有在爬虫场景判断url 是否已经被爬取过
       这种方案可以加在第一种方案中,在缓存之前在加一层 BloomFilter ,在查询的时候先去 BloomFilter 去查询 key 是否存在,如果不存在就直接返回,存在再走查缓存 -> 查 DB。
  1.3.3如何选择 针对于一些恶意攻击,攻击带过来的大量key 是不存在的,那么我们采用第一种方案就会缓存大量不存在key的数据。此时我们采用第一种方案就不合适了,我们完全可以先对使用第二种方案进行过滤掉这些key     
        针对这种key异常多、请求重复率比较低的数据,我们就没有必要进行缓存,使用第二种方案直接过滤掉,而对于空数据的key有限的,重复率比较高的,我们则可以采用第一种方式进行缓存

 

布隆过滤 参考

https://www.cnblogs.com/z941030/p/9218356.html

布隆过滤代码

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        // 预计插入的元素总数
//        int expectedInsertions = Integer.MAX_VALUE>>4;
        int expectedInsertions = 50000000;
        // 期望误判率
        double fpp = 0.0000000001;

        BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), expectedInsertions, fpp);
        bloomFilter.put("asd");
        bloomFilter.put("asd1");
        bloomFilter.put("asd2");
        //判断是否包含
        System.out.println(bloomFilter.mightContain("asd3"));
        System.out.println(System.currentTimeMillis()-start);


        BloomFilter<Email> emailBloomFilter = BloomFilter.create(new Funnel<Email>() {
            @Override
            public void funnel(Email from, PrimitiveSink into) {
                into.putString(from.getDomain(), Charsets.UTF_8);
            }
        }, expectedInsertions, fpp);


        System.out.println(new Email("xiaominb","haha").hashCode());
        System.out.println(new Email("xiaominb","haha").hashCode());


        emailBloomFilter.put(new Email("xiaominb","haha"));
        System.out.println(emailBloomFilter.mightContain(new Email("xiaominb", "haha")));;
    }

 

2.缓存击穿


 2.1什么是缓存击穿 当一个key过期是此时有大量的请求过来,那么这些请求会就落到DB这个现象称为缓存击穿
 2.2缓存击穿带来的问题,会造成DB压力过大可能宕机
 2.3缓存击穿解决方案,可以通过互斥锁,多个线程同时去查询数据库,那么可以在第一个查询数据的线程用一个互斥锁来锁住他,其他线程走到这一步拿不到锁就等着,等第一个线程查询结束了,然后做缓存,后面的线程进来发现有缓存了就直接走缓存
    代码如下:互斥锁

 /**
     * 互斥锁 针对缓存击穿方案
     * @param key
     * @return
     * @throws InterruptedException
     */
    String mutex(String key) throws InterruptedException {
        String value = redisTemplate.opsForValue().get(key).toString();
        if (value  == null) {
            // 设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
            if (redisTemplate.opsForValue().setIfAbsent(key+"_mutex","1",3,TimeUnit.MINUTES)) {

                // 数据库获取值
//                value = db.get(key);
                redisTemplate.opsForValue().set(key, value);
                redisTemplate.delete(key+"_mutex");
            } else {
                //其他线程休息50毫秒后重试
                Thread.sleep(50);
                redisTemplate.opsForValue().get(key);
            }
            return value;
        } else {
            return value;
        }
    }

 

3缓存雪崩


 3.1什么是缓存雪崩 当某一刻发生大规模缓存失效的时候,比如缓存服务宕机或者大量的key失效,则会有大量的请求落到DB上面
 3.2缓存雪崩带来的问题,会造成DB压力过大可能宕机
 3.3缓存雪崩解决方案
  3.3.1缓存服务宕机
    3.3.1.1 事前使用集群报错缓存服务的高可用
    3.3.1.2 事中使用本地缓存+Hystrix限流&降级,避免MySQL被打死 
            使用 ehcache 本地缓存的目的也是考虑在 Redis Cluster 完全不可用的时候,ehcache 本地缓存还能够支撑一阵。
            使用 Hystrix进行限流 & 降级 ,比如一秒来了5000个请求,我们可以设置假设只能有一秒 2000个请求能通过这个组件,那么其他剩余的 3000 请求就会走限流逻辑。
            然后去调用我们自己开发的降级组件(降级),比如设置的一些默认值呀之类的。以此来保护最后的 MySQL 不会被大量的请求给打死。

    3.3.1.3 开启redis持久化,尽快恢复服务
  3.3.2大量key失效
       缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件    

 

本文参考:https://mp.weixin.qq.com/s/7gbJCeBKklTlAxU_vsrIxg

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值