数学建模社区-数学中国

标题: 【转】BloomFilter——大规模数据处理利器 [打印本页]

作者: 我要吃章鱼丸子    时间: 2016-4-8 12:03
标题: 【转】BloomFilter——大规模数据处理利器
那些优雅的数据结构(1) : BloomFilter——大规模数据处理利器Posted on 2011-01-02 19:08 苍梧 阅读(37504) 评论(25) 编辑 收藏
/ q5 |. K; u7 a0 k) N
原文 http://www.cnblogs.com/heaad/archive/2011/01/02/1924195.html
BloomFilter——大规模数据处理利器
  Bloom Filter是由Bloom在1970年提出的一种多哈希函数映射的快速查找算法。通常应用在一些需要快速判断某个元素是否属于集合,但是并不严格要求100%正确的场合。
1 ^5 S+ }& A# x% C1 g
. 实例
  为了说明Bloom Filter存在的重要意义,举一个实例:
  假设要你写一个网络蜘蛛(web crawler)。由于网络间的链接错综复杂,蜘蛛在网络间爬行很可能会形成“环”。为了避免形成“环”,就需要知道蜘蛛已经访问过那些URL。给一个URL,怎样知道蜘蛛是否已经访问过呢?稍微想想,就会有如下几种方案:
  1. 将访问过的URL保存到数据库。
  2. 用HashSet将访问过的URL保存起来。那只需接近O(1)的代价就可以查到一个URL是否被访问过了。
  3. URL经过MD5或SHA-1等单向哈希后再保存到HashSet或数据库。
  4. Bit-Map方法。建立一个BitSet,将每个URL经过一个哈希函数映射到某一位。
  方法1~3都是将访问过的URL完整保存,方法4则只标记URL的一个映射位。

; g" H& b* e! |9 s( b
  以上方法在数据量较小的情况下都能完美解决问题,但是当数据量变得非常庞大时问题就来了。
  方法1的缺点:数据量变得非常庞大后关系型数据库查询的效率会变得很低。而且每来一个URL就启动一次数据库查询是不是太小题大做了?
  方法2的缺点:太消耗内存。随着URL的增多,占用的内存会越来越多。就算只有1亿个URL,每个URL只算50个字符,就需要5GB内存。
  方法3:由于字符串经过MD5处理后的信息摘要长度只有128Bit,SHA-1处理后也只有160Bit,因此方法3比方法2节省了好几倍的内存。
  方法4消耗内存是相对较少的,但缺点是单一哈希函数发生冲突的概率太高。还记得数据结构课上学过的Hash表冲突的各种解决方法么?若要降低冲突发生的概率到1%,就要将BitSet的长度设置为URL个数的100倍。
  实质上上面的算法都忽略了一个重要的隐含条件:允许小概率的出错,不一定要100%准确!也就是说少量url实际上没有没网络蜘蛛访问,而将它们错判为已访问的代价是很小的——大不了少抓几个网页呗。
7 Z4 [- e8 A; r' e/ O6 h( U0 c' w5 ?
. Bloom Filter的算法
  废话说到这里,下面引入本篇的主角——Bloom Filter。其实上面方法4的思想已经很接近Bloom Filter了。方法四的致命缺点是冲突概率高,为了降低冲突的概念,Bloom Filter使用了多个哈希函数,而不是一个。
    Bloom Filter算法如下:
    创建一个m位BitSet,先将所有位初始化为0,然后选择k个不同的哈希函数。第i个哈希函数对字符串str哈希的结果记为h(i,str),且h(i,str)的范围是0到m-1 。
- a# R1 W/ h0 u6 @$ i$ v8 T
(1) 加入字符串过程
  下面是每个字符串处理的过程,首先是将字符串str“记录”到BitSet中的过程:
  对于字符串str,分别计算h(1,str),h(2,str)…… h(k,str)。然后将BitSet的第h(1,str)、h(2,str)…… h(k,str)位设为1。
  图1.Bloom Filter加入字符串过程
  很简单吧?这样就将字符串str映射到BitSet中的k个二进制位了。
! t+ j3 M5 }) w9 L7 h
(2) 检查字符串是否存在的过程

( |! C) b1 z: p! w
  下面是检查字符串str是否被BitSet记录过的过程:
  对于字符串str,分别计算h(1,str),h(2,str)…… h(k,str)。然后检查BitSet的第h(1,str)、h(2,str)…… h(k,str)位是否为1,若其中任何一位不为1则可以判定str一定没有被记录过。若全部位都是1,则“认为”字符串str存在。

$ G3 U3 M+ p5 @9 [; u
  若一个字符串对应的Bit不全为1,则可以肯定该字符串一定没有被Bloom Filter记录过。(这是显然的,因为字符串被记录过,其对应的二进制位肯定全部被设为1了)
  但是若一个字符串对应的Bit全为1,实际上是不能100%的肯定该字符串被Bloom Filter记录过的。(因为有可能该字符串的所有位都刚好是被其他字符串所对应)这种将该字符串划分错的情况,称为false positive 。
7 ?  A1 _: O& k
(3) 删除字符串过程
   字符串加入了就被不能删除了,因为删除会影响到其他字符串。实在需要删除字符串的可以使用Counting bloomfilter(CBF),这是一种基本Bloom Filter的变体,CBF将基本Bloom Filter每一个Bit改为一个计数器,这样就可以实现删除字符串的功能了。

$ \0 g5 H9 e0 L9 A
  Bloom Filter跟单哈希函数Bit-Map不同之处在于:Bloom Filter使用了k个哈希函数,每个字符串跟k个bit对应。从而降低了冲突的概率。
- \) n9 u& X" |) k
. Bloom Filter参数选择

  m7 ?. z$ @1 |7 v. r
   (1)哈希函数选择
     哈希函数的选择对性能的影响应该是很大的,一个好的哈希函数要能近似等概率的将字符串映射到各个Bit。选择k个不同的哈希函数比较麻烦,一种简单的方法是选择一个哈希函数,然后送入k个不同的参数。
   (2)Bit数组大小选择
     哈希函数个数k、位数组大小m、加入的字符串数量n的关系可以参考参考文献1。该文献证明了对于给定的m、n,当 k = ln(2)* m/n 时出错的概率是最小的。
     同时该文献还给出特定的k,m,n的出错概率。例如:根据参考文献1,哈希函数个数k取10,位数组大小m设为字符串个数n的20倍时,false positive发生的概率是0.0000889 ,这个概率基本能满足网络爬虫的需求了。  

6 o3 B/ m. z7 b: H! z* Q$ ?  s9 f
. Bloom Filter实现代码
    下面给出一个简单的Bloom Filter的Java实现代码:

6 f! q# g2 |0 I& ~. @6 X( g) I2 ?[url=][/url]) G, ~5 \5 v8 E; k; g2 O
import java.util.BitSet;7 F% h, d5 Z, n1 v5 J6 I

" L% L6 F6 r+ `4 S% Epublicclass BloomFilter 0 f# G2 m( }) M1 n
{
( w, z1 T: A- a: t, h$ n( |5 k/* BitSet初始分配2^24个bit */
8 j9 W0 r9 |5 i5 r6 a! [privatestaticfinalint DEFAULT_SIZE =1<<25; 1 u1 G3 ?& ?3 T8 B: g+ a6 d5 [
/* 不同哈希函数的种子,一般应取质数 */$ z. Q6 o/ S: P# p0 v
privatestaticfinalint[] seeds =newint[] { 5, 7, 11, 13, 31, 37, 61 };# E1 \8 R/ [7 L9 v& q, l1 D
private BitSet bits =new BitSet(DEFAULT_SIZE);$ p9 y2 i% j  y+ G( A" E
/* 哈希函数对象 */
: L6 [( p9 V+ E' x  X# ?, Xprivate SimpleHash[] func =new SimpleHash[seeds.length];
$ a6 }) G3 j1 n( r4 s) N) N9 C4 L# T) J" S/ v; p0 f' H
public BloomFilter() ! a: t* Q; e6 {$ w" b
{
5 x4 d: E: t+ }5 U- g3 G8 Cfor (int i =0; i < seeds.length; i++)/ J' {* e+ I" e
{
0 |; g9 A3 s( I: y1 ]! x6 Mfunc =new SimpleHash(DEFAULT_SIZE, seeds);
  K- T1 s7 e; m1 I+ K}
5 F) e6 o9 R1 R8 u! `4 v}7 b3 m* t- ~) V; t, l# ^

% T9 _: \% }' {; d// 将字符串标记到bits中" \1 n" i% U( H& t  i; P
publicvoid add(String value)
2 i" b' {1 t7 m/ o' J" N. h5 Q{
$ K( x* h1 l$ K; ^9 \7 e3 Ufor (SimpleHash f : func)   e9 G( X5 r" Y4 s0 T( e
{" h( Q, p2 }' \  X8 \2 P/ i4 K, J
bits.set(f.hash(value), true);+ l, [4 n) ^/ W
}+ I  O. X  V, F% n# n
}
2 m+ p+ O0 G% o3 `; V/ z
7 S- v9 E/ l! G# _+ P1 F  j  Q//判断字符串是否已经被bits标记# b+ G1 j9 l  d: ?' a
publicboolean contains(String value)
1 B/ S" ]0 M; y5 D( c, [1 T( v{
( ^8 f7 p1 }8 ~  C1 {3 \0 Aif (value ==null) 1 Y2 B$ u% y1 {' g6 N( l3 J% ^
{
& G  q& R3 W) r" L2 x( E% `returnfalse;
% A3 D! V. P) u' [}
; j* g* V! C% Z7 U( t' Mboolean ret =true;% `  T' c4 N8 j
for (SimpleHash f : func)
8 {) i+ p; I, ~{
% |: C( ?' K+ iret = ret && bits.get(f.hash(value));
  U  I6 J) v3 o/ R$ x}
. r' M/ G% C, Greturn ret;( R3 E0 y4 ]1 O
}1 X+ d& j; [1 s5 ~" _( E

0 _; _8 l$ k+ E% {( @" K7 f8 Q/* 哈希函数类 */
% E8 r* P: I7 S$ n) N- zpublicstaticclass SimpleHash
7 F4 M0 n! r- x3 h3 `  _: w2 E. {{- P0 `* Z. M  ]# n: ^, B
privateint cap;
" x5 H5 g# Y- ~  sprivateint seed;  E8 W4 k, b. q& K& N5 I( h
9 H9 s2 V0 l# ?8 d* G
public SimpleHash(int cap, int seed) 6 u% ~% p4 I7 z9 ^9 I
{
5 m/ U! @! ?1 v7 b' T! |this.cap = cap;3 u. |1 q+ o7 n, s
this.seed = seed;7 M. U$ Z- D( k/ u4 W5 Q/ k! N
}
8 F7 Y1 c6 |. q, a, X
. g# ^& f6 |* T" Q( n/ @5 M//hash函数,采用简单的加权和hash( X: L# h4 S2 ^4 V% _+ D5 W" f3 R+ j
publicint hash(String value) % K- }" i( e! L
{! P, `" ~7 W8 n( h
int result =0;
4 t, E! Q* F+ Z1 s0 d9 b2 uint len = value.length();
+ \2 N/ b" C! q* N8 u. K0 Bfor (int i =0; i < len; i++) % z. l% O% V) ]# a, c# y; [3 n
{
2 Q; B( M4 Z) B1 M, e/ \1 W) V: Oresult = seed * result + value.charAt(i);
9 g9 a7 Z2 I$ @) `}
4 `4 g2 j) e) X: u) ]return (cap -1) & result;
0 R  s# Z9 X- \! Q- F6 p7 ]/ ?+ D2 i}
$ T# I) z7 e1 j, }}; {8 m8 _# Z4 }
}
1 d  n' _- A) r4 g

* n/ m+ w) R6 o5 L# }[url=][/url]
3 d5 L9 `1 r# ^3 F) g' a' r
# B6 b- V0 P, k1 ~- w2 |% [3 }5 y8 v& h; `3 \6 V

  J+ [1 D3 {) Q: A( ~
# `4 K$ m* q4 c. j! r% {
参考文献:
* P7 y& P7 l. V# ~# ~% P
[1]Pei Cao. Bloom Filters - the math.
http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
[2]Wikipedia. Bloom filter.
1 V/ Y* n5 v2 q! a
& ]- y; ]* Y* I# `, ]6 s2 Z$ q$ T3 R
( D" T. _% A7 f. P# I
8 @% U, q; P, }/ c0 K8 X! z





欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5