java布隆过滤器实现数据是否存在的判断

当需要判断某个数据在数据库是否存在的时候(比如判断某条记录的id是否存在),如果数据量小的时候,可以直接从数据库进行查询,当数据量非常大,而且查询又很频繁的时候,频繁的查询就会成为性能瓶颈。此时就可以考虑使用布隆过滤器来提高查询效率。布隆过滤器可以判断 “某样东西一定不存在或者可能存在”。

大致的思路是先保存数据的hash值的映射,判断数据是否存在时,先计算hash值,然后再判断对应的hash值的映射是否存在。

1. 单节点内存布隆过滤器

1.1 添加依赖

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>

1.2 测试代码

package com.demo;

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

public class BloomFilterDemo {

    private static int size = 1000000;//预计要插入多少数据

    private static double fpp = 0.01;//期望的误判率

    private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, fpp);

    public static void main(String[] args) {
        //插入数据
        for (int i = 0; i < 1000000; i++) {
            bloomFilter.put(i);
        }
        int count = 0;
        for (int i = 1000000; i < 2000000; i++) {
            if (bloomFilter.mightContain(i)) {
                count++;
                System.out.println(i + "误判了");
            }
        }
        System.out.println("总共的误判数:" + count);
    }
}

1.3 执行测试

1999640误判了
1999697误判了
1999827误判了
1999942误判了
总共的误判数:10314

单节点布隆过滤器不适合多节点集群环境下的应用共享,将数据保存在内存中,系统重启也会导致数据丢失。

2. redis布隆过滤器

redis具有访问速度快等特点。

2.1 添加依赖

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>

2.2 测试代码

package com.demo;

import com.google.common.hash.Funnels;
import com.google.common.hash.Hashing;
import redis.clients.jedis.Jedis;

import java.nio.charset.Charset;

public class RedisBloomFilter {

    static final int expectedInsertions = 1000000 * 100;//要插入多少数据
    static final double fpp = 0.001;//期望的误判率

    //bit数组长度
    private static long numBits;

    //hash函数数量
    private static int numHashFunctions;

    static {
        numBits = optimalNumOfBits(expectedInsertions, fpp);
        numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        for (int i = 0; i < 100000; i++) {
            long[] indexs = getIndexs(String.valueOf(i));
            for (long index : indexs) {
                jedis.setbit("codebear:bloom", index, true);
            }
        }

        long start = System.currentTimeMillis();

        for (int i = 200000; i < 300001; i++) {
            long[] indexs = getIndexs(String.valueOf(i));
            boolean norepeat = true;

            for (long index : indexs) {
                Boolean isContain = jedis.getbit("codebear:bloom", index);
                if (!isContain) {
                    norepeat = true;
                    // System.out.println(i + "肯定没有重复");
                    break;
                } else {
                    norepeat = false;
                }
            }

            if (norepeat == true) {
                System.out.println(i + "肯定没有重复");
            } else {
                System.out.println(i + "可能重复");
            }

        }

        long end = System.currentTimeMillis();

        System.out.print("time spend is:" + (end - start));
    }

    /**
     * 根据key获取bitmap下标
     */
    private static long[] getIndexs(String key) {
        long hash1 = hash(key);
        long hash2 = hash1 >>> 16;
        long[] result = new long[numHashFunctions];
        for (int i = 0; i < numHashFunctions; i++) {
            long combinedHash = hash1 + i * hash2;
            if (combinedHash < 0) {
                combinedHash = ~combinedHash;
            }
            result[i] = combinedHash % numBits;
        }
        return result;
    }

    private static long hash(String key) {
        Charset charset = Charset.forName("UTF-8");
        return Hashing.murmur3_128().hashObject(key, Funnels.stringFunnel(charset)).asLong();
    }

    //计算hash函数个数
    private static int optimalNumOfHashFunctions(long n, long m) {
        return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
    }

    //计算bit数组长度
    private static long optimalNumOfBits(long n, double p) {
        if (p == 0) {
            p = Double.MIN_VALUE;
        }
        return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
    }
}

2.3 执行测试

299998肯定没有重复
299999肯定没有重复
300000肯定没有重复
time spend is:5774

10万个数据的判断,总共耗时5774毫秒,相当于1毫秒可以进行20次判断。

通过将数据保存到redis中,可以实现多个节点之间的数据共享,借助于redis的持久化机制,即使redis重启,仍然可以保证重启之后,数据不丢失。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值