布隆過濾器介紹
布隆過濾器(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());