布隆过滤器介绍以及实现

1.什么是布隆过滤器(Bloom-Filter)

介绍:Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。

常用应用场景:当并发量很大并且无效请求较多时,很容易造成缓存击穿,使用布隆过滤器来过滤无效请求。

2.布隆过滤器实现原理。

第一步将库中已存在的数据取出;

第二步通过多个相互独立的hash函数将取出的数据进行计算;

第三步将结果映射到bit数组中,第四步输入要过滤的值;

第五步重复第二步中的hash计算;

第六步将结果和bit数组中的值进行比对,假如bit数组中为1的位置hash计算结果为0,则说明数据不存在。

布隆过滤器示意图

例子:如果库中的值为“123”,hash结果映射到bit数组中的结果是全1。当输入值为“123”,计算hash映射后发现,对应的bit数组中存在0,则说明数据不存在。

优缺点:优点是空间效率和查询时间都远超过一般的算法,缺点是有一定的误识别率和删除困难。布隆过滤器说没有的数据肯定没有,但是返回存在的数据却不一定存在。

3. 布隆过滤器支持对数据的删除吗?

传统的布隆过滤器不支持删除操作。然而其衍生过滤器“Counting Bloom filter”支持元素删除。

4. 如何选择哈希函数个数和布隆过滤器长度

布隆过滤器长度不能过小,当较小时bit数组中的所有位很容易全为1,所有的输入值都会返回数据存在。所以长度不能过小,但过大也会影响查询效率。

哈希函数的个数也需要权衡,个数越多则布隆过滤器 bit 位置位 1 的速度越快,且布隆过滤器的效率越低;但是如果太少的话,那误报率又会变高。

如何选择适合业务的 k 和 m 值呢,常用公式如下:

k 为哈希函数个数,m 为布隆过滤器长度,n 为插入的元素个数,p 为误报率。

6.布隆过滤器的大Value问题

生成环境中建议对体积庞大的布隆过滤器进行拆分。拆分的形式方法多种多样,但是本质是不要将 Hash(Key) 之后的请求分散在多个节点的多个小 bitmap 上,而是应该拆分成多个小 bitmap 之后,对一个 Key 的所有哈希函数都落在这一个小 bitmap 上。

7.布隆过滤器Java实现

import java.util.BitSet;
import java.util.concurrent.atomic.AtomicInteger;

public class BloomFilterPractice {

    private final BitSet bitSet;
    private final AtomicInteger useCount = new AtomicInteger();

    public BloomFilterPractice() {
        this( 16);
    }

    /**
     * @param size 过滤器长度
     */
    public BloomFilterPractice(int size) {
        //每个字符串需要的bit位数*总数据量
        long bitSize = size;
        if (bitSize < 0 || bitSize > Integer.MAX_VALUE) {
            throw new RuntimeException("长度溢出");
        }
        //创建一个BitSet位集合
        bitSet = new BitSet(size);
    }

    private Double getUseRate() {
        return (double) useCount.intValue() / (double) bitSet.length();
    }
    
    private int getHashValue(String data, int hashNmber){
        int hashCode = data.hashCode();
        int hash = hashCode ^ (hashCode >>> 7) ^ (hashCode >>> 4);
        hash = hash * hashNmber % bitSet.length();
        return Math.abs(hash);
    }

    public Boolean inputValue(String value){
        int[] indexs = new int[16];
        int[] hashNumber = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
        //假定data已经存在
        boolean exist = true;
        int index;
        for (int i = 0; i < indexs.length; i++) {
            //计算位hash值
            indexs[i] = index = getHashValue(value, hashNumber[i]);
            if (exist) {
                //如果某一位bit不存在,则说明该data不存在
                if (!bitSet.get(index)) {
                    exist = false;
                    setTrue(index);
                }
            } else {
                //如果不存在则直接置为true
                setTrue(index);
            }
        }
        return exist;
    }

    private void setTrue(int index) {
        useCount.incrementAndGet();
        bitSet.set(index, true);
    }

    public static void main(String[] args) {
        BloomFilterPractice bloomFilterPractice = new BloomFilterPractice();
        System.out.println(bloomFilterPractice.inputValue("中国联通"));
        System.out.println(bloomFilterPractice.inputValue("中国电信"));
        System.out.println(bloomFilterPractice.inputValue("中国移动"));
        System.out.println(bloomFilterPractice.inputValue("谁最厉害"));
        System.out.println(bloomFilterPractice.inputValue("谁最厉害"));
        System.out.println(bloomFilterPractice.getUseRate());
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章