日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

BloomFilter–大规模数据处理利器(转)

發(fā)布時(shí)間:2025/3/21 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BloomFilter–大规模数据处理利器(转) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

BloomFilter–大規(guī)模數(shù)據(jù)處理利器

  Bloom Filter是由Bloom在1970年提出的一種多哈希函數(shù)映射的快速查找算法。通常應(yīng)用在一些需要快速判斷某個(gè)元素是否屬于集合,但是并不嚴(yán)格要求100%正確的場(chǎng)合。

一.?實(shí)例

  為了說明Bloom Filter存在的重要意義,舉一個(gè)實(shí)例:

  假設(shè)要你寫一個(gè)網(wǎng)絡(luò)爬蟲程序(web crawler)。由于網(wǎng)絡(luò)間的鏈接錯(cuò)綜復(fù)雜,爬蟲在網(wǎng)絡(luò)間爬行很可能會(huì)形成“環(huán)”。為了避免形成“環(huán)”,就需要知道爬蟲程序已經(jīng)訪問過那些URL。給一個(gè)URL,怎樣知道爬蟲程序是否已經(jīng)訪問過呢?稍微想想,就會(huì)有如下幾種方案:

  1.?將訪問過的URL保存到數(shù)據(jù)庫(kù)。

  2.?用HashSet將訪問過的URL保存起來。那只需接近O(1)的代價(jià)就可以查到一個(gè)URL是否被訪問過了。

  3. URL經(jīng)過MD5或SHA-1等單向哈希后再保存到HashSet或數(shù)據(jù)庫(kù)。

  4. Bit-Map方法。建立一個(gè)BitSet,將每個(gè)URL經(jīng)過一個(gè)哈希函數(shù)映射到某一位。

  方法1~3都是將訪問過的URL完整保存,方法4則只標(biāo)記URL的一個(gè)映射位。

  以上方法在數(shù)據(jù)量較小的情況下都能完美解決問題,但是當(dāng)數(shù)據(jù)量變得非常龐大時(shí)問題就來了。

  方法1的缺點(diǎn):數(shù)據(jù)量變得非常龐大后關(guān)系型數(shù)據(jù)庫(kù)查詢的效率會(huì)變得很低。而且每來一個(gè)URL就啟動(dòng)一次數(shù)據(jù)庫(kù)查詢是不是太小題大做了?

  方法2的缺點(diǎn):太消耗內(nèi)存。隨著URL的增多,占用的內(nèi)存會(huì)越來越多。就算只有1億個(gè)URL,每個(gè)URL只算50個(gè)字符,就需要5GB內(nèi)存。

  方法3:由于字符串經(jīng)過MD5處理后的信息摘要長(zhǎng)度只有128Bit,SHA-1處理后也只有160Bit,因此方法3比方法2節(jié)省了好幾倍的內(nèi)存。

  方法4消耗內(nèi)存是相對(duì)較少的,但缺點(diǎn)是單一哈希函數(shù)發(fā)生沖突的概率太高。還記得數(shù)據(jù)結(jié)構(gòu)課上學(xué)過的Hash表沖突的各種解決方法么?若要降低沖突發(fā)生的概率到1%,就要將BitSet的長(zhǎng)度設(shè)置為URL個(gè)數(shù)的100倍。

二. Bloom Filter的算法

???廢話說到這里,下面引入本篇的主角–Bloom Filter。其實(shí)上面方法4的思想已經(jīng)很接近Bloom Filter了。方法四的致命缺點(diǎn)是沖突概率高,為了降低沖突的概念,Bloom Filter使用了多個(gè)哈希函數(shù),而不是一個(gè)。

?? Bloom Filter算法如下:

???創(chuàng)建一個(gè)m位BitSet,先將所有位初始化為0,然后選擇k個(gè)不同的哈希函數(shù)。第i個(gè)哈希函數(shù)對(duì)字符串str哈希的結(jié)果記為h(i,str),且h(i,str)的范圍是0到m-1?。

(1)?加入字符串過程

  下面是每個(gè)字符串處理的過程,首先是將字符串str“記錄”到BitSet中的過程:

  對(duì)于字符串str,分別計(jì)算h(1,str),h(2,str)…… h(k,str)。然后將BitSet的第h(1,str)、h(2,str)…… h(k,str)位設(shè)為1。

?

  很簡(jiǎn)單吧?這樣就將字符串str映射到BitSet中的k個(gè)二進(jìn)制位了。

(2)?檢查字符串是否存在的過程

  下面是檢查字符串str是否被BitSet記錄過的過程:

  對(duì)于字符串str,分別計(jì)算h(1,str),h(2,str)…… h(k,str)。然后檢查BitSet的第h(1,str)、h(2,str)…… h(k,str)位是否為1,若其中任何一位不為1則可以判定str一定沒有被記錄過。若全部位都是1,則“認(rèn)為”字符串str存在。

  若一個(gè)字符串對(duì)應(yīng)的Bit不全為1,則可以肯定該字符串一定沒有被Bloom Filter記錄過。(這是顯然的,因?yàn)樽址挥涗涍^,其對(duì)應(yīng)的二進(jìn)制位肯定全部被設(shè)為1了)

  但是若一個(gè)字符串對(duì)應(yīng)的Bit全為1,實(shí)際上是不能100%的肯定該字符串被Bloom Filter記錄過的。(因?yàn)橛锌赡茉撟址乃形欢紕偤檬潜黄渌址鶎?duì)應(yīng))這種將該字符串劃分錯(cuò)的情況,稱為false positive?。

(3)?刪除字符串過程

???字符串加入了就被不能刪除了,因?yàn)閯h除會(huì)影響到其他字符串。實(shí)在需要?jiǎng)h除字符串的可以使用Counting bloomfilter(CBF),這是一種基本Bloom Filter的變體,CBF將基本Bloom Filter每一個(gè)Bit改為一個(gè)計(jì)數(shù)器,這樣就可以實(shí)現(xiàn)刪除字符串的功能了。

  Bloom Filter跟單哈希函數(shù)Bit-Map不同之處在于:Bloom Filter使用了k個(gè)哈希函數(shù),每個(gè)字符串跟k個(gè)bit對(duì)應(yīng)。從而降低了沖突的概率。

三. Bloom Filter參數(shù)選擇

?(1)哈希函數(shù)選擇

???  哈希函數(shù)的選擇對(duì)性能的影響應(yīng)該是很大的,一個(gè)好的哈希函數(shù)要能近似等概率的將字符串映射到各個(gè)Bit。選擇k個(gè)不同的哈希函數(shù)比較麻煩,一種簡(jiǎn)單的方法是選擇一個(gè)哈希函數(shù),然后送入k個(gè)不同的參數(shù)。

(2) m,n,k值,我們?nèi)绾稳≈?/p>

我們定義:

可能把不屬于這個(gè)集合的元素誤認(rèn)為屬于這個(gè)集合(False Positive)

不會(huì)把屬于這個(gè)集合的元素誤認(rèn)為不屬于這個(gè)集合(False Negative)。

?

哈希函數(shù)的個(gè)數(shù)k、位數(shù)組大小m、加入的字符串?dāng)?shù)量n的關(guān)系。哈希函數(shù)個(gè)數(shù)k取10,位數(shù)組大小m設(shè)為字符串個(gè)數(shù)n的20倍時(shí),false positive發(fā)生的概率是0.0000889?,即10萬(wàn)次的判斷中,會(huì)存在9次誤判,對(duì)于一天1億次的查詢,誤判的次數(shù)為9000次。?

?

算法分析:

我們假設(shè)kn<m且各個(gè)哈希函數(shù)是完全隨機(jī)的。當(dāng)集合S={x1, x2,…,xn}的所有元素都被k個(gè)哈希函數(shù)映射到m位的位數(shù)組中時(shí),這個(gè)位數(shù)組中某一位還是0的概率是:

False Positive的概率是:

p’表示1的概率,k次方表示8次hash都為1的概率。

?當(dāng)?k = ln 2 * m/n 時(shí),右邊的等式值最小,此時(shí)等式轉(zhuǎn)變成:

?

?四. Bloom Filter實(shí)現(xiàn)代碼(簡(jiǎn)易版)

?? 下面給出一個(gè)簡(jiǎn)單的Bloom Filter的Java實(shí)現(xiàn)代碼:

package org.magnus.utils; import java.util.BitSet; //傳統(tǒng)的Bloom filter 不支持從集合中刪除成員。 //Counting Bloom filter由于采用了計(jì)數(shù),因此支持remove操作。 //基于BitSet來實(shí)現(xiàn),性能上可能存在問題 public class SimpleBloomFilter {//DEFAULT_SIZE為2的25次方private static final int DEFAULT_SIZE = 2 << 24;/* 不同哈希函數(shù)的種子,一般應(yīng)取質(zhì)數(shù),seeds數(shù)據(jù)共有7個(gè)值,則代表采用7種不同的HASH算法 */private static final int[] seeds = new int[] { 5, 7, 11, 13, 31, 37, 61 };//BitSet實(shí)際是由“二進(jìn)制位”構(gòu)成的一個(gè)Vector。假如希望高效率地保存大量“開-關(guān)”信息,就應(yīng)使用BitSet.//BitSet的最小長(zhǎng)度是一個(gè)長(zhǎng)整數(shù)(Long)的長(zhǎng)度:64位private BitSet bits = new BitSet(DEFAULT_SIZE);/* 哈希函數(shù)對(duì)象 */private SimpleHash[] func = new SimpleHash[seeds.length];public static void main(String[] args) {String value = "stone2083@yahoo.cn";//定義一個(gè)filter,定義的時(shí)候會(huì)調(diào)用構(gòu)造函數(shù),即初始化七個(gè)hash函數(shù)對(duì)象所需要的信息。SimpleBloomFilter filter = new SimpleBloomFilter();//判斷是否包含在里面。因?yàn)闆]有調(diào)用add方法,所以肯定是返回falseSystem.out.println(filter.contains(value));filter.add(value);System.out.println(filter.contains(value));}//構(gòu)造函數(shù)public SimpleBloomFilter() {for (int i = 0; i < seeds.length; i++) {//給出所有的hash值,共計(jì)seeds.length個(gè)hash值。共7位。//通過調(diào)用SimpleHash.hash(),可以得到根據(jù)7種hash函數(shù)計(jì)算得出的hash值。//傳入DEFAULT_SIZE(最終字符串的長(zhǎng)度),seeds[i](一個(gè)指定的質(zhì)數(shù))即可得到需要的那個(gè)hash值的位置。func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);}}// 將字符串標(biāo)記到bits中,即設(shè)置字符串的7個(gè)hash值函數(shù)為1public void add(String value) {for (SimpleHash f : func) {bits.set(f.hash(value), true);}}//判斷字符串是否已經(jīng)被bits標(biāo)記public boolean contains(String value) {//確保傳入的不是空值if (value == null) {return false;}boolean ret = true;//計(jì)算7種hash算法下各自對(duì)應(yīng)的hash值,并判斷for (SimpleHash f : func) {//&&是boolen運(yùn)算符,只要有一個(gè)為0,則為0。即需要所有的位都為1,才代表包含在里面。//f.hash(value)返回hash對(duì)應(yīng)的位數(shù)值//bits.get函數(shù)返回bitset中對(duì)應(yīng)position的值。即返回hash值是否為0或1。ret = ret && bits.get(f.hash(value));}return ret;}/* 哈希函數(shù)類 */public static class SimpleHash {//cap為DEFAULT_SIZE的值,即用于結(jié)果的最大的字符串長(zhǎng)度。//seed為計(jì)算hash值的一個(gè)給定key,具體對(duì)應(yīng)上面定義的seeds數(shù)組private int cap;private int seed;public SimpleHash(int cap, int seed) {this.cap = cap;this.seed = seed;}//計(jì)算hash值的具體算法,hash函數(shù),采用簡(jiǎn)單的加權(quán)和hashpublic int hash(String value) {//int的范圍最大是2的31次方減1,或超過值則用負(fù)數(shù)來表示int result = 0;int len = value.length();for (int i = 0; i < len; i++) {//數(shù)字和字符串相加,字符串轉(zhuǎn)換成為ASCII碼result = seed * result + value.charAt(i);//System.out.println(result+"--"+seed+"*"+result+"+"+value.charAt(i));}// System.out.println("result="+result+";"+((cap - 1) & result));// System.out.println(414356308*61+'h'); 執(zhí)行此運(yùn)算結(jié)果為負(fù)數(shù),為什么?//&是java中的位邏輯運(yùn)算,用于過濾負(fù)數(shù)(負(fù)數(shù)與進(jìn)算轉(zhuǎn)換成反碼進(jìn)行)。return (cap - 1) & result;}} }

五:Bloom Filter的優(yōu)點(diǎn)及應(yīng)用。

1.2????優(yōu)缺點(diǎn)分析

1.2.1????????優(yōu)點(diǎn):

節(jié)約緩存空間(空值的映射),不再需要空值映射。

減少數(shù)據(jù)庫(kù)或緩存的請(qǐng)求次數(shù)。

提升業(yè)務(wù)的處理效率以及業(yè)務(wù)隔離性。

?

1.2.2????????缺點(diǎn):

存在誤判的概率。

傳統(tǒng)的Bloom Filter不能作刪除操作。

?

1.3????使用場(chǎng)景

?????????? 適用于特定場(chǎng)景,能夠有效的解決數(shù)據(jù)庫(kù)空查問題。

?????????以公司的某小表查詢?yōu)槔?#xff0c;該表每天查詢量20億次左右,且數(shù)據(jù)庫(kù)中存在大量的下面的空查:

???????? 目前表中的記錄為8w,即n的值為8w, m=20*n=160w,占用空間大小195KB。以type||CONTENT復(fù)合鍵作為key值,假設(shè)HASH次數(shù)k取值為6,誤判率為:0.0303%(10000次中存在3次誤判)。HASH次數(shù)的最優(yōu)解為14,當(dāng)k=14時(shí),誤判率為:0.014%(10000次中存在1-2次誤判)。

測(cè)試過程及結(jié)果如下(源代碼見附件):

測(cè)試場(chǎng)景1:m=1600000;n=80000;最優(yōu)解k=14;m/n=20;k的次數(shù)為:6;對(duì)1000w數(shù)據(jù)進(jìn)行判定:

?

???????

?測(cè)試結(jié)果:

2000w數(shù)據(jù)誤判的記錄為:3035,誤判率約為0.03035%(和理論值0.0303%相差不大)。判斷2000萬(wàn)數(shù)據(jù)的時(shí)間為25秒。平均一次判斷時(shí)間為:2.5微秒。平均一次hash時(shí)間為0.417微秒。

測(cè)試場(chǎng)景2:m=1600000;n=80000;最優(yōu)解k=14;m/n=20;k的次數(shù)為:6;對(duì)2000w數(shù)據(jù)進(jìn)行判定:????測(cè)試結(jié)果:2000w數(shù)據(jù)誤判的記錄為:5839,誤判率約為0.029%(理論值為0. 0303%)。判斷1000萬(wàn)數(shù)據(jù)的時(shí)間為51秒。平均一次判斷時(shí)間為:2.55微秒。平均一次hash時(shí)間為0.425微秒。
測(cè)試場(chǎng)景3:m=1600000;n=80000;最優(yōu)解k=14;m/n=20;k的次數(shù)為:14;對(duì)1000w數(shù)據(jù)進(jìn)行判定?:??測(cè)試結(jié)果:1000w數(shù)據(jù)誤判的記錄為:605,誤判率約為0.00605%(和理論值0. 014%相差不大)。判斷1000萬(wàn)數(shù)據(jù)的時(shí)間為37秒。平均一次判斷時(shí)間為:3.7微秒。平均一次hash時(shí)間為0.265微秒。?

?

測(cè)試場(chǎng)景4:m=1600000;n=80000;最優(yōu)解k=14;m/n=20;k的次數(shù)為:14;對(duì)2000w數(shù)據(jù)進(jìn)行判定:?測(cè)試結(jié)果:2000w數(shù)據(jù)誤判的記錄為:1224,誤判率約為0.00612%(理論值為0.014%)。判斷1000萬(wàn)數(shù)據(jù)的時(shí)間為84秒。平均一次判斷時(shí)間為:4.2微秒。平均一次hash時(shí)間為0.3微秒。

?

?????????其它測(cè)試略。

?

結(jié)論:

測(cè)試m/nK(括號(hào)內(nèi)為最優(yōu)解)數(shù)據(jù)基數(shù)誤判數(shù)誤判率理論值用時(shí)(單位:秒)一次判定時(shí)間(單位:微秒)一次Hash時(shí)間(單位:微秒.估參考)
1206(14)1000W30350.03035%0.0303%252.50.417
2206(14)2000W58390.029%0.0303%512.550.425
32014(14)1000W6050.00605%0.014%373.70.265
42014(14)2000W12240.00612%0.014%844.20.3
52020(14)1000W9140.00914%不計(jì)算484.80.24
62020(14)2000W18810.00941%不計(jì)算994.950.2475
7107(7)1000w5178540.786%0.819%414.10.59
853(3)1000w9014119.014%9.2%313.11.033
921(1)1000w391072639.107%39.3%292.92.9
1022(1)1000w396106539.61%40%303.03.0
1125(1)1000w643669664.37%不計(jì)算767.61.52

?

一次判斷時(shí)間計(jì)算方式為:總時(shí)間/總次數(shù)

一次HASH所需時(shí)間計(jì)算方式為:一次判定時(shí)間/每次判斷需要的hash數(shù)。

一次HASH所需時(shí)間,當(dāng)執(zhí)行hash次數(shù)越少,基數(shù)越小,誤差越大。當(dāng)一次判斷所需的hash次數(shù)越大時(shí),一次hash時(shí)間越精確。

?

結(jié)論:

m/n的比值越大越好,比較越大,誤判率會(huì)越代,但同時(shí)會(huì)使用更多的空間成本。

???????? Hash次數(shù)增加帶來的收益并不大。需要在條件允許的情況下,盡量的擴(kuò)大m/n的值。

?

六:實(shí)施方案思考

?????????適用于一些黑名單,垃圾郵件等的過濾。

?????????當(dāng)位數(shù)組較小時(shí),可以作本地jvm緩存。

當(dāng)位數(shù)組較大時(shí),可以做基于tair的緩存,此時(shí)可能需要開辟單獨(dú)的應(yīng)用來提供查詢支持。

?????????此方案,適用的應(yīng)用場(chǎng)景需要能夠容忍,位數(shù)組和的延時(shí)。

轉(zhuǎn)載于:https://www.cnblogs.com/bendantuohai/p/4708039.html

總結(jié)

以上是生活随笔為你收集整理的BloomFilter–大规模数据处理利器(转)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。