redis缓存穿透和处理方案

4 篇文章 0 订阅

1.什么是缓存穿透?

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

2.为什么要用缓存?

1.提升应用程序性能,

2.降低数据库成本

3.减少后端负载

4.可预测的性能

5.消除数据库热点

6.提高读取吞吐量(IOPS)

3.缓存的设计

Insert:新增数据至数据库

update:删除缓存中对应的数据,修改数据库的数据

delete:删除缓存中对应数据,少出数据库的数据

query: 查询缓存中数据,存在数据机返回查询的数据,

缓存中不存在数据则查询数据库,数据库存在数据则返回数据库中的数据,并同步更新到缓存

缓存和数据库都不存在数据,返回null。

4.缓存穿透问题

1.如果查询的数据在缓存和数据库中都不存在,会带来额外的IO操作和开销

2.如果该用户操作是一个偏执狂,无限如此操作怎么办?key value -->null

3.如果此时有鳄鱼攻击者已经发现你的系统漏洞,频繁用不存在数据的key进行请求怎么办?

5.如何咋大数据集合中判断某个元素不存在?

1.容器的选择

1)空间占用小

2)能支持快速查找

2.算法选择

1).确定性(计算多次的结果一致)

2).允许任意的输入且保证固定的长度的输出

利用哈希算法

3.哈希函数的问题分析

这样会产生一种误判,即:Andy其实并没有你在位图中,但是通过hash计算时,返回了存在的结果,这时可以增加位图长度,以及多次哈希计算来解决

6. Boom Filter (不存在漏报,存在误报)

主要是要计算出,m和k的值,以及hash算法

public class TestBoomFilter {

    //预期存储的数据量
    private int n;

    //误判率
    private double p;  //0.0003

    // bit[] length
    private int m;

    // hash函数个数
    private int k;

    // bit[]
    private BitSet bitMap;


    /**
     * 构造器
     * @param n  存储的目标数据
     * @param p  误判率
     */
    public TestBoomFilter(int n, double p) {
        this.n = n;
        this.p = p;
    }


    /**
     * 添加元素
     *
     * @param element
     */
    public void addElement(String element) {

        //lazy策略
        if (bitMap == null) {
            //init操作
            init();
        }



        int[] posArr = getIndexes(element);


        for (int temPos : posArr) {
            bitMap.set(temPos,true);
        }

    }

    /**
     * 判断element元素是否存在在与集合中.
     * @param element  判断的元素
     * @return
     */
    public boolean isExist(String element) {
        int[] posArr = getIndexes(element);

        boolean flag=true;
        for (int temPos : posArr) {
            flag= flag && bitMap.get(temPos);
        }
        return flag;
    }

    /**
     * 获取K个函数计算element的值的下标数组
     *
     * @param element
     * @return
     */
    private int[] getIndexes(String element) {

        int[] retArr = new int[this.k];

        for (int i = 0; i < this.k; i++) {
            retArr[i] = HashUtil.MD5Hash(element + i) % this.m;
        }
        return retArr;
    }

    /**
     * 初始化过程.
     */
    private synchronized void init() {

        if (this.m == 0) {
            //计算m的值..
            this.m = (int) ((-n * Math.log(this.p)) / (Math.log(2) * Math.log(2)));
        }

        if (this.k == 0) {
            this.k = Math.max(1, (int) Math.round(this.m / this.n * Math.log(2)));
        }

        if (bitMap == null) {
            bitMap = new BitSet(this.m);
        }

        System.out.println("this.m : " + this.m);
        System.out.println("this.k : " + this.k);
    }


    /**
     * 测试自写的boomfilter
     */
    private static void testMyBoomFilter(){
        AndyBoomFilter boomFilter = new TestBoomFilter(1000000,0.0003);

        for (int i = 0; i <1000000; i++) {
            boomFilter.addElement("abc"+i);
        }

        int count = 0;
        for (int i = 0; i <2000000; i++) {
          if(boomFilter.isExist("abc"+i)) {
              count++;
          }
        }
        System.out.println("count:"+count);
    }


    /**
     * 测试guava的boomfilter
     */
    private static void testGuavaBoomFilter(){
        BloomFilter<String> bommfilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000000, 0.0003);

        for (int i = 0; i <1000000; i++) {
            bommfilter.put("abc"+i);
        }

        int count = 0;
        for (int i = 0; i <2000000; i++) {
                if(bommfilter.mightContain("abc"+i)) {
                    count++;
                }
        }
        System.out.println("count:"+count);
    }



    public static void main(String[] args) {
        testMyBoomFilter();
//        testGuavaBoomFilter();
    }

}

hash算法

public static int MD5Hash(String key) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("md5");
            byte[] bytes = key.getBytes();
            md5.update(bytes);
            BigInteger bi = new BigInteger(md5.digest());
            return Math.abs(bi.intValue());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return -1;
    }

结果:

7.源码分析

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>21.0</version>
        </dependency>
        <dependency>
            <groupId>com.baqend</groupId>
            <artifactId>bloom-filter</artifactId>
            <version>1.0.7</version>
        </dependency>

计算m和k

    @VisibleForTesting
    static int optimalNumOfHashFunctions(long n, long m) {
        return Math.max(1, (int)Math.round((double)m / (double)n * Math.log(2.0D)));
    }

    @VisibleForTesting
    static long optimalNumOfBits(long n, double p) {
        if (p == 0.0D) {
            p = 4.9E-324D;
        }

        return (long)((double)(-n) * Math.log(p) / (Math.log(2.0D) * Math.log(2.0D)));
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值