Skip to content

Commit 7a1980a

Browse files
committed
[Feature] add for new
1 parent 0b93b27 commit 7a1980a

7 files changed

+1471
-0
lines changed
Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
---
2+
3+
title: Bloom Filter
4+
date: 2018-12-05
5+
categories: [Althgorim]
6+
tags: [althgorim, sh]
7+
published: true
8+
excerpt: Bloom Filter 布隆过滤器算法
9+
---
10+
11+
# Bloom Filter
12+
13+
布隆过滤器(英语:Bloom Filter)是1970年由布隆提出的。
14+
15+
它实际上是一个很长的二进制向量和一系列随机映射函数。
16+
17+
布隆过滤器可以用于检索一个元素是否在一个集合中。
18+
19+
它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
20+
21+
![布隆过滤器](https://p3-tt.byteimg.com/origin/dfic-imagehandler/78523def-3c95-4b69-bf22-56281132af9a?from=pc)
22+
23+
## 布隆过滤器速成
24+
25+
布隆过滤器在本质上是二进制向量。在高层级上,布隆过滤器以下面的方式工作:
26+
27+
添加元素到过滤器。
28+
29+
对元素进行几次哈希运算,当索引匹配哈希的结果时,将该位设置为 1 的。
30+
31+
如果要检测元素是否属于集合,使用相同的哈希运算步骤,检查相应位的值是1还是0。这就是布隆过滤器明确元素不存在的过程。如果位未被设置,则元素绝不可能存在于集合中。
32+
33+
当然,一个肯定的答案意味着,要不就是元素存在于集合中,要不就是遇见了**哈希冲突**
34+
35+
> 注意
36+
37+
1. 一个值,设置了多个 hash,也就是存储了多个位置。这样为了提高准确性。
38+
39+
## 特点
40+
41+
所以布隆过滤有以下几个特点:
42+
43+
1. 只要返回数据不存在,则肯定不存在。
44+
45+
2. 返回数据存在,但只能是大概率存在。
46+
47+
3. 同时不能清除其中的数据。
48+
49+
# 应用场景
50+
51+
判断大量元素中包含一个数:
52+
53+
网页爬虫对URL的去重,避免爬取相同的URL地址;
54+
55+
反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱(同理,垃圾短信);
56+
57+
缓存击穿,将已存在的缓存放到布隆中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉。
58+
59+
使用布隆过滤器优化SQL查询
60+
61+
# Guava BloomFliter 使用
62+
63+
## 引入 jar
64+
65+
```xml
66+
<dependency>
67+
<groupId>com.google.guava</groupId>
68+
<artifactId>guava</artifactId>
69+
<version>14.0.1</version>
70+
</dependency>
71+
```
72+
73+
## 测试 demo
74+
75+
- Person.java
76+
77+
```java
78+
public class Person {
79+
80+
public int id;
81+
public String firstName;
82+
public String lastName;
83+
public int birthYear;
84+
85+
public Person(int id, String firstName, String lastName, int birthYear) {
86+
this.id = id;
87+
this.firstName = firstName;
88+
this.lastName = lastName;
89+
this.birthYear = birthYear;
90+
}
91+
}
92+
```
93+
94+
- BloomFilter.java
95+
96+
```java
97+
import com.google.common.base.Charsets;
98+
import com.google.common.hash.BloomFilter;
99+
import com.google.common.hash.Funnel;
100+
import com.google.common.hash.PrimitiveSink;
101+
import org.junit.Test;
102+
103+
import java.util.Arrays;
104+
import java.util.List;
105+
106+
public class BloomFilterTest {
107+
108+
Funnel<Person> personFunnel = new Funnel<Person>() {
109+
@Override
110+
public void funnel(Person person, PrimitiveSink into) {
111+
into.putInt(person.id)
112+
.putString(person.firstName, Charsets.UTF_8)
113+
.putString(person.lastName, Charsets.UTF_8)
114+
.putInt(person.birthYear);
115+
}
116+
};
117+
118+
@Test
119+
public void test() {
120+
BloomFilter<Person> friends = BloomFilter.create(personFunnel, 500, 0.01);
121+
for (Person friend : personList()) {
122+
friends.put(friend);
123+
}
124+
125+
Person dude = new Person(1, "1", "1", 1);
126+
127+
// much later
128+
if (friends.mightContain(dude)) {
129+
// the probability that dude reached this place if he isn't a friend is 1%
130+
// we might, for example, start asynchronously loading things for dude while we do a more expensive exact check
131+
System.out.println("也许存在哦。。。");
132+
}
133+
}
134+
135+
private List<Person> personList() {
136+
Person person = new Person(1, "1", "1", 1);
137+
Person person2 = new Person(2, "1", "1", 1);
138+
Person person3 = new Person(3, "1", "1", 1);
139+
Person person4 = new Person(4, "1", "1", 1);
140+
141+
return Arrays.asList(person, person2, person3, person4);
142+
}
143+
144+
}
145+
```
146+
147+
测试结果输出:
148+
149+
```
150+
也许存在哦。。。
151+
```
152+
153+
# 算法原理和复杂度
154+
155+
参考 [wiki](https://en.wikipedia.org/wiki/Bloom_filter)
156+
157+
158+
# 回顾
159+
160+
上一节我们简单介绍了 BloomFilter 的原理,并且介绍了 guava BloomFilter 的使用。
161+
162+
今天让我们更上一层楼,实现一个属于自己的 BoolFilter。
163+
164+
![思维导图](https://p6-tt.byteimg.com/origin/pgc-image/347ec620e13349569dc9f2dc09f00723?from=pc)
165+
166+
# 实现原理
167+
168+
## 原理回顾
169+
170+
布隆过滤器在本质上是二进制向量。
171+
172+
在高层级上,布隆过滤器以下面的方式工作:
173+
174+
添加元素到过滤器。
175+
176+
对元素进行几次哈希运算,当索引匹配哈希的结果时,将该位设置为 1 的。
177+
178+
如果要检测元素是否属于集合,使用相同的哈希运算步骤,检查相应位的值是1还是0。这就是布隆过滤器明确元素不存在的过程。如果位未被设置,则元素绝不可能存在于集合中。
179+
180+
当然,一个肯定的答案意味着,要不就是元素存在于集合中,要不就是遇见了哈希冲突。
181+
182+
## 实现思路
183+
184+
我们再加入一个元素时,通过多种不同的 hash 算法,然后设置到 bit 数组中。
185+
186+
判断是否存在的时候,也对元素采用多种不同的 hash 算法,如果都为 true,则说明可能存在。如果有不为 true 的,说明一定不不存在。
187+
188+
![源码](https://p3-tt.byteimg.com/origin/pgc-image/069c7d29e2874585bb574cc3516bd0f6?from=pc)
189+
190+
# 自己实现简单版本
191+
192+
## hash 算法
193+
194+
可以参考 [hash 算法介绍](https://houbb.github.io/2018/05/30/hash)
195+
196+
- IHash.java
197+
198+
```java
199+
public interface IHash {
200+
201+
/**
202+
* 根据 key 获取对应的hash值
203+
* @param key 字符串
204+
* @return hash 值
205+
*/
206+
int hashIndex(final String key);
207+
208+
}
209+
```
210+
211+
三个简单的实现:
212+
213+
- HashOne
214+
215+
```java
216+
public class HashOne implements IHash {
217+
218+
@Override
219+
public int hashIndex(String key) {
220+
int hash = 0;
221+
for(int i = 0; i < key.length(); i++) {
222+
hash = 33 * hash + key.charAt(i);
223+
}
224+
225+
return Math .abs(hash);
226+
}
227+
228+
}
229+
```
230+
231+
- HashTwo
232+
233+
```java
234+
public class HashTwo implements IHash {
235+
236+
@Override
237+
public int hashIndex(String key) {
238+
final int p = 16777619;
239+
int hash = (int) 2166136261L;
240+
241+
for(int i = 0 ; i < key.length(); i++) {
242+
hash = (hash ^ key.charAt(i)) * p;
243+
}
244+
hash += hash << 13;
245+
hash ^= hash >> 7;
246+
hash += hash << 3;
247+
hash ^= hash >> 17;
248+
hash += hash << 5;
249+
return Math.abs(hash);
250+
}
251+
}
252+
```
253+
254+
- HashThree
255+
256+
```java
257+
public class HashThree implements IHash {
258+
259+
@Override
260+
public int hashIndex(String key) {
261+
int hash, i;
262+
263+
for (hash = 0, i = 0; i < key.length(); ++i) {
264+
hash += key.charAt(i);
265+
hash += (hash << 10);
266+
hash ^= (hash >> 6);
267+
}
268+
269+
hash += (hash << 3);
270+
hash ^= (hash >> 11);
271+
hash += (hash << 15);
272+
return Math.abs(hash);
273+
}
274+
}
275+
```
276+
277+
## Bloom Fliter 实现
278+
279+
- IBloomFilter.java
280+
281+
```java
282+
public interface IBloomFilter {
283+
284+
/**
285+
* 添加一个元素
286+
* @param key 元素
287+
*/
288+
void add(final String key);
289+
290+
/**
291+
* 可能包含元素
292+
* @param key 元素
293+
* @return 可能包含
294+
*/
295+
boolean mightContains(final String key);
296+
297+
}
298+
```
299+
300+
- SimpleBloomFilter.java
301+
302+
```java
303+
import com.github.houbb.guava.learn.bloom.hash.HashOne;
304+
import com.github.houbb.guava.learn.bloom.hash.HashThree;
305+
import com.github.houbb.guava.learn.bloom.hash.HashTwo;
306+
307+
public class SimpleBloomFilter implements IBloomFilter {
308+
309+
private final int size;
310+
311+
private boolean[] array;
312+
313+
public SimpleBloomFilter(int size) {
314+
this.size = size;
315+
this.array = new boolean[size];
316+
}
317+
318+
@Override
319+
public void add(String key) {
320+
// 做三次 hash
321+
int hashOne = new HashOne().hashIndex(key);
322+
int hashTwo = new HashTwo().hashIndex(key);
323+
int hashThree = new HashThree().hashIndex(key);
324+
325+
this.array[hashOne%array.length] = true;
326+
this.array[hashTwo%array.length] = true;
327+
this.array[hashThree%array.length] = true;
328+
}
329+
330+
@Override
331+
public boolean mightContains(String key) {
332+
// 做三次 hash
333+
int hashOne = new HashOne().hashIndex(key);
334+
int hashTwo = new HashTwo().hashIndex(key);
335+
int hashThree = new HashThree().hashIndex(key);
336+
337+
if(!array[hashOne]) {
338+
return false;
339+
}
340+
if(!array[hashTwo]) {
341+
return false;
342+
}
343+
if(!array[hashThree]) {
344+
return false;
345+
}
346+
347+
return true;
348+
}
349+
}
350+
```
351+
352+
353+
# 小结
354+
355+
当然我们实现版本的性能比较差,可以参考下 guava 的实现。
356+
357+
本文简单介绍了布隆过滤器的发展历史和应用场景,并给出了 guava BloomFliter 使用案例。希望大家对 bloom filter 有一个最基本的认识。
358+
359+
下一节我们将和大家一起实现属于自己的布隆过滤器。
360+
361+
觉得本文对你有帮助的话,欢迎点赞评论收藏关注一波。你的鼓励,是我最大的动力~
362+
363+
不知道你有哪些收获呢?或者有其他更多的想法,欢迎留言区和我一起讨论,期待与你的思考相遇。
364+
365+
# 参考资料
366+
367+
[布隆过滤器](https://zh.wikipedia.org/wiki/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8)
368+
369+
[如何判断一个元素在亿级数据中是否存在?](https://mp.weixin.qq.com/s/aMp0xoGPlY0XmJORzTmUZw)
370+
371+
- Guava
372+
373+
[Google Guava 中的布隆过滤](https://www.oschina.net/translate/guava-bloomfilter?print)
374+
375+
[GUava hash](https://github.com/google/guava/wiki/HashingExplained)
376+
377+
- Bit 数组
378+
379+
https://blog.csdn.net/zimu666/article/details/8284906
380+
381+
https://blog.csdn.net/zimu666/article/details/8295724
382+
383+
https://www.cnblogs.com/varlxj/p/5168157.html
384+
385+
https://yuhuang-neil.iteye.com/blog/1190574
386+

0 commit comments

Comments
 (0)