BloomFilte 布隆過濾器原理與實現

布隆過濾器介紹

布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的算法,缺點是有一定的誤識別率和刪除困難

爲什麼要用布隆過濾器?

遇到判重系統和緩存穿透時,伴隨數據量很大,數十億甚至更多,內存裝不下且數據庫檢索又極慢的情況,考慮下布隆過濾器,因爲它是一個空間效率佔用極少和查詢時間極快的算法,但是需要業務可以忍受一個判斷失誤率。

應用

1、Google Guava 中的布隆過濾與布隆持久化

布隆過濾器

/**
 * Funnel,這是Guava中定義的一個接口,它和PrimitiveSink配套使用,
 * 主要是把任意類型的數據轉化成Java基本數據類型(primitive value,如char,byte,int……),
 * expectedInsertions 期望插入數據數,int或long
 * 默認用java.nio.ByteBuffer實現,最終均轉化爲byte數組
 * fpp期望誤判率,比如1E-7(千萬分之一)
 */
BloomFilter<String> b = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000, 0.000001);
b.put("121");
b.put("122");
b.put("123");
System.out.println(b.mightContain("12321"));
BloomFilter<String> b1 = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000, 0.000001);
b1.put("aba");
b1.put("abb");
b1.put("abc");
b1.putAll(b);
System.out.println(b1.mightContain("123"));

 

布隆持久化

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

import java.io.*;
import java.nio.charset.Charset;
import java.util.Properties;

public class BloomFilterUtil {


    public final static String  bloomPath = "Bloom";

    public static BloomFilter<CharSequence> create() {
        /**
         * Funnel,這是Guava中定義的一個接口,它和PrimitiveSink配套使用,
         * 主要是把任意類型的數據轉化成Java基本數據類型(primitive value,如char,byte,int……),
         * expectedInsertions 期望插入數據數,int或long
         * 默認用java.nio.ByteBuffer實現,最終均轉化爲byte數組
         * fpp期望誤判率,比如1E-7(千萬分之一)
         */
        BloomFilter<CharSequence> filter = BloomFilter.create(
                Funnels.stringFunnel(Charset.forName("utf-8")),
                5000000,
                0.00001);
        return filter;
    }

    /**
     * 持久化的數據加載到布隆
     *
     * @param path
     * @return
     */
    public static BloomFilter<CharSequence> readFrom(String path) {
        /**
         * Funnel,這是Guava中定義的一個接口,它和PrimitiveSink配套使用,
         * 主要是把任意類型的數據轉化成Java基本數據類型(primitive value,如char,byte,int……),
         * expectedInsertions 期望插入數據數,int或long
         * 默認用java.nio.ByteBuffer實現,最終均轉化爲byte數組
         * fpp期望誤判率,比如1E-7(千萬分之一)
         */
        BloomFilter<CharSequence> filter = BloomFilterUtil.create();

        if (null == path || path.isEmpty()) {
            return filter;
        }
        try {
            //將之前持久化的數據加載到Filter
            BufferedInputStream in = new BufferedInputStream(new FileInputStream(path));
            filter = BloomFilter.readFrom(in, Funnels.stringFunnel(Charset.forName("utf-8")));
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return filter;
    }

    /**
     * 布隆持久化
     *
     * @param bloomFilter
     * @param bloomPath
     * @throws IOException
     */
    public static void writeTo(BloomFilter bloomFilter, String bloomPath) throws IOException {
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(bloomPath));
        try {
            //數據持久化到本地
            bloomFilter.writeTo(out);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

 

2、基於Redis的分佈式布隆過濾器

Redis+Redission

Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.2.129:6379");
RedissonClient client = Redisson.create(config);
RBloomFilter<Object> bloomFilter = client.getBloomFilter("bf2");
bloomFilter.tryInit(10, 0.01);
bloomFilter.add("4");
bloomFilter.add("3");
bloomFilter.add("2");
bloomFilter.add("1");
boolean exists = bloomFilter.isExists();
boolean contains = bloomFilter.contains("3");
System.out.println("----1------"+exists);
System.out.println("-----2-----"+contains);
RAtomicLong aLong = client.getAtomicLong("redisson-aa");
aLong.set(100L);
System.out.println("-----3----"+aLong.get());

 

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