zoukankan      html  css  js  c++  java
  • 缓存雪崩和缓存穿透的现象和解决方案+boomfilter的引入

    缓存雪崩和缓存穿透的现象和解决方案
    1.什么是缓存雪崩
    由于各种原因,redis集群整体宕机,缓存不可用,导致所有的请求都直接发到数据库,数据库抗不了这种高并发直接挂掉,数据库挂掉,就可能引发整体系统不可用.
    2.缓存雪崩的解决方案
    事前:redis做好高可用架构
    事中:开启本地ehcache缓存+hystrix/sentinal熔断 限流 降级的保护机制,保证数据库不会被请求搞挂机,一个请求顺序为:先查ehcache->redis->hystrix/sentinal保护机制->mysql
    事后:redis开启持久化机制(RDB/AOF),保证缓存挂了可以根据磁盘文件快速恢复

    3.什么是缓存穿透
    外来的黑客攻击,发起大量数据库没有的id进行查询,这样既然数据库都没有,缓存肯定没有,就是直接穿过缓存查询数据库,大量请求过来也会把数据库搞挂机

    4.缓存穿透的解决方案
    很简单,缓存空值,如果数据库没查到的值,直接根据id在缓存中设置一个NUKNOW的值,这样下次请求就直接走缓存.
    但是这样也会出现一个问题,缓存会存储大量的空值,以及数据查询还会走数据库,可以在加一层BoomFilter(布隆过滤器),具体原理就是先把数据库的数据装载一份到boomfilter,查询的时候先判断redis中该值在不在,如果不在就通过boomfilter进行判断是否在数据库中,判断也没有就直接返回空值.这样可以避免大量的缓存空值,也能有效避免黑客攻击,不好的地方就是:
    代码复杂度增大,
    需要另外维护一个集合来存放缓存的Key,
    布隆过滤器不支持删值操作
    布隆过滤器有一定的误判率,想要误判率越小,那么所需的空间越大
    不过正确的数据都是可以通过的,对于不存在的key,可能会出现误判,就是返回也存在数据库的情况,这个可以根据实际情况进行调整误判的概率;

    附boomfilter的示例代码:

    引入依赖

            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>23.0</version>
            </dependency>
    import com.google.common.hash.BloomFilter;
    import com.google.common.hash.Funnels;
    
    import java.math.BigDecimal;
    
    public class BloomFilterTest {
        private static int size = 1000000;
        //fpp代表误判率控制在多少
        private static BloomFilter<Integer> bf = BloomFilter.create(Funnels.integerFunnel(), size,0.01);
    
        public static void main(String[] args) {
            // 初始化1000000条数据到过滤器中
            for (int i = 0; i < size; i++) {
                bf.put(i);
            }
    
            int failCount=0;
            // 匹配已在过滤器中的值,是否有匹配不上的
            for (int i = 0; i < size; i++) {
                if (!bf.mightContain(i)) {
                    System.out.println("未匹配上的数据");
                    failCount++;
                }
            }
            System.out.println("未匹配上的正确数据量:"+failCount);
    
            // 匹配不在过滤器中的10000个值,有多少匹配出来
            int count = 0;
            for (int i = size; i < size + 10000; i++) {
                if (bf.mightContain(i)) {
                    count++;
                }
            }
            BigDecimal bigDecimal = new BigDecimal(count);
            BigDecimal divide = bigDecimal.divide(new BigDecimal(10000), 5, BigDecimal.ROUND_HALF_UP);
            System.out.println("误命中的数量:" + count+",误命中的概率:"+divide);
    
        }
    }
  • 相关阅读:
    桂林印象
    快变
    近期的事
    *C#中使用ref和out一点认识!*
    *在框架集页面放置TreeView控件时页面跳转的问题解决*
    *无法找到脚本库的问题*
    *Ajax.Net快速入门*
    *网页过期*
    *Prototype开发笔记*
    *正则表达式*
  • 原文地址:https://www.cnblogs.com/bin-zhao/p/14361227.html
Copyright © 2011-2022 走看看