布隆过滤器BloomFilter原理剖析

来源:互联网 发布:淘宝网浅秋女羊毛衫 编辑:程序博客网 时间:2024/06/06 19:20

场景:
不安全网页的黑名单包含100亿个黑名单网页,每个网页URL最多占用64B。现在设计系统根据网页URL判断该网页是否在黑名单上。

要求:该系统允许万分之一以下的判断失误率,且使用的额外空间不要超过30GB。

分析:如果单纯通过数据库或者哈希表来保存下来,需要640G的空间,不满足要求。

遇到网页黑名单系统、垃圾邮件过滤系统、爬虫的网址判重系统、两份URL文件的重复URL等场景,又看到系统容忍一定程度的失误率,但是对空间要求比较严格,那么可以采取布隆过滤器来解决。

一个布隆过滤器精确地代表一个集合,并可以精确判断一个元素是否在集合中,优势在于使用很少的空间就能将准确率做到很高的程度。


布隆过滤器的原理分析:

一个URL经过K个哈希函数计算,结果相互独立,对计算出来的每个结果都对m取余,然后在长度为m的bit类型数组上把相应的位置设置为1(涂黑)

检查过程:

一个URL经过上述过程后,得到k个在[0,m-1]范围上的值。

如果有一个不为1(不为黑),则该URL肯定不在这个集合里。

如果都是1(均被涂黑),则该URL在这个集合里面,但是可能会出现误判。(输入对象过多,但是bitMap长度过小,导致bitMap绝大部分都被涂黑,可能出现误判)


布隆过滤器的设计:

输入个数n和允许失误率----------过滤器大小m和哈希函数个数k(具体推导公式和证明看左程云《程序员代码面试指南》P303)

布隆过滤器会有误报,针对于已经发现的误报样本可以通过建立白名单来防止误报。


下面是一个具体布隆过滤器的代码实现。(不是上述题目)

假设采取k=8个散列函数,m=1<<22 长度的bitMap数组,判断字符串是否在出现在文件中。

import java.util.BitSet;import java.io.*;/** * Created by flh on 2017/8/28. */public class BloomFilter {    //DEFAULT_SIZE为2的22次方,即此处的1左移22位    private static final int DEFAULT_SIZE = 1<<22;    /*     * 不同哈希函数的种子,一般取质数     * seeds数组共有8个值,则代表采用8种不同的哈希函数     */    private int[] seeds = new int[]{3, 5, 7, 11, 13, 31, 37, 61};    /*     * 初始化一个给定大小的位集     * BitSet实际是由“二进制位”构成的一个Vector。     * 假如希望高效率地保存大量“开-关”信息,就应使用BitSet.     */    private BitSet bitSets = new BitSet(DEFAULT_SIZE);    //构建hash函数对象    private SimpleHash[] hashFuns = new SimpleHash[seeds.length];    //布隆过滤器配置文件存放路径    private String path = "";    public BloomFilter(String path){        /**         *  给出所有的hash值,共计seeds.length个hash值。共8位。         *  通过调用SimpleHash.hash(),可以得到根据8种hash函数计算得出hash值。         *  传入DEFAULT_SIZE(最终字符串的长度),seeds[i](一个指定的质数)即可得到需要的那个hash值的位置。         */        for(int i=0; i<seeds.length; i++){            hashFuns[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);        }        //配置文件路径地址        this.path = path;    }    /**     *     * 方法名:add     * 描述:将给定的字符串标记到bitSets中,即设置字符串的8个函数值的位置为1     * @param value     */    public synchronized void add(String value){        for(SimpleHash hashFun : hashFuns){            bitSets.set(hashFun.hash(value), true);        }    }    /**     *     * 方法名:isExit     * 描述:判断给定的字符串是否已经存在在bloofilter中,如果存在返回true,不存在返回false     * @param value     * @return     */    public synchronized boolean isExit(String value){        //判断传入的值是否为null        if(null == value){            return false;        }        for(SimpleHash hashFun : hashFuns){            if(!bitSets.get(hashFun.hash(value))){                //如果判断8个hash函数值中有一个位置不存在即可判断为不存在Bloofilter中                return false;            }        }        return true;    }    /**     *     * 方法名:init     * 描述:读取配置文件     */    public void init(){        File file = new File(path);        FileInputStream in = null;        try {            in = new FileInputStream(file);            read(in);        }catch(Exception e){            e.printStackTrace();        }finally{            try {                if(in!=null){                    in.close();                    in = null;                }            } catch (IOException e) {                e.printStackTrace();            }        }    }    /**     *     * 方法名:read     * 描述:根据传入的流,初始化bloomfilter     * @param in     */    private void read(InputStream in){        if(null == in){ //如果in为null,则返回            return;        }        InputStreamReader reader = null;        try {            //创建输入流            reader = new InputStreamReader(in, "UTF-8");            BufferedReader buffReader = new BufferedReader(reader, 512);            String theWord = null;            do {                theWord = buffReader.readLine();                //如果theWord不为null和空,则加入Bloomfilter中                if(theWord!=null && !theWord.trim().equals("")){                    add(theWord);                }            } while (theWord != null);        } catch (IOException e){            e.printStackTrace();        } finally{            //关闭流            try {                if(reader != null){                    reader.close();                    reader = null;                }                if(in != null){                    in.close();                    in = null;                }            } catch (IOException e) {                e.printStackTrace();            }        }    }    public static void main(String[] args) {        BloomFilter bloomFilterTest = new BloomFilter("e:/test.txt");        bloomFilterTest.init();        System.out.println(bloomFilterTest.isExit("spark"));        System.out.println(bloomFilterTest.isExit("Storm"));    }    public static class SimpleHash {        /*         * cap为DEFAULT_SIZE,即用于结果的最大字符串的值         * seed为计算hash值的一个key值,具体对应上文中的seeds数组         */        private int cap;        private int seed;        public SimpleHash(int cap, int seed){            this.cap = cap;            this.seed = seed;        }        /**         *         * 方法名:hash         * 描述:计算hash的函数,用户可以选择其他更好的hash函数         * @param value         * @return         */        public int hash(String value){            int result = 0;            int length = value.length();            for(int i=0; i<length; i++){                result = seed*result + value.charAt(i);            }            return (cap-1) & result;        }    }}

假如文本文件test.txt文件中内容如下:

spark1helpStorm

输出结果为:

falsetrue



原创粉丝点击