大規模なデータ処理とキャッシュ浸透の2つのシナリオでブルームフィルターを知ることができました。いくつかの資料を調べてそれを理解しましたが、既存の資料の多くはニーズを満たしていないため、ブルームに関する記事をまとめることにしました。記事をフィルタリングします。この記事を通して、より多くの人がブルームフィルターを理解し、実際に使用することを願っています!
ブルームフィルター
1.ブルームフィルターとは何ですか?
布隆过滤器:它是由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的比较巧妙的概率性数据结构。
- この機能は効率的な挿入とクエリですが、削除するのは簡単ではなく、返される結果は確率的です。
同時に、特定のデータが存在してはならず(キャッシュで使用してキャッシュの侵入を防ぎ、キーがデータベースに存在するかどうかを判断できます)、特定のデータが存在している可能性があります!
従来のリスト、マップ、セットと比較して、より効率的で、占有するスペースが少なくなります。 - 使用シナリオ:ブルームフィルターは、小さなメモリを使用して大きなデータを処理し、データが存在する(存在してはならない、存在する可能性がある)かどうかを保存して判断
し、ビット配列の各要素は1ビットのみを占有し、各要素はこれは1または0なので、100万要素に適用されるビット配列は1000000Bit / 8 = 125000Byte = 125000/1024 kb≈122kbしか占有しません。極端に小さいのですが、その動作原理を分析してみましょう。
2.ブルームフィルターの原理の紹介。
- 要素がブルームフィルターに追加されると、次の操作が実行されます。
- ブルームフィルタリングハッシュ関数を使用して要素を計算し、ハッシュ値を取得します(いくつかのハッシュ値を持ついくつかのハッシュ関数があります)。
- 得られたハッシュ値に従って、ビット配列の対応するインデックスの値が1に設定されます。
- ブルームフィルターにデータが存在するかどうかを判断する必要がある場合:
- 判定されたデータに対して同じハッシュ関数を再度実行し、
- ハッシュ値を取得した後、ビット配列内の対応する添え字のすべての値が1かどうかを判断します。値が1の場合は、データがブルームフィルターにあることを意味し、1以外の値がある場合は、データが存在しないことを意味しますブルームフィルター。
簡単な例として:
図に示すように、ビット配列が初期化されるとき、すべての位置は0です。文字列がブルームフィルターに追加されるとき、文字列は異なるハッシュ関数を介して異なるハッシュ値を生成します。次に、ハッシュ値に対応するビット配列添え字の要素を1に設定します。
文字列がブルームフィルターにあるかどうかを判断する必要がある場合は、特定の文字列に対して同じハッシュ計算を再度実行するだけでよく、値を取得した後、値がビット配列の各要素が1かどうかを判断します。どちらも1です。これは、この値がブルームフィルターにあることを意味します。1以外の値がある場合は、要素がブルームフィルターにないことを意味します。
異なる文字列が同じハッシュ値を取得し、複数のハッシュの位置が同じになる場合があります。この場合、ビット配列のサイズを適切に増やして、ハッシュ関数(数値、アルゴリズム)を調整できます。
要約すると、ブルームフィルターは要素が存在することを示しており、わずかな確率で誤った判断をすることになります。ブルームフィルターは、要素が存在しないことを示しているため、要素は存在しない必要があります。
3.ブルームフィルターの使用シナリオ。
- 指定されたデータが存在するかどうかを判断します。
- 数値が5億を超える大きな数値を含む数値セットに含まれているかどうかを判別します。
- キャッシュの侵入を防ぐ(要求キーのデータはキャッシュをバイパスし、データベースに直接作用して、キーがデータベースに存在するかどうかを判断します)
- メールスパムフィルタリング、ブラックリスト機能。
- 重複排除:
- たとえば、特定のURLをクロールする場合、クロールされたURLは重複排除されます。
4. Javaプログラミングを介してブルームフィルターを手動で実装します。
以下では、ブルームフィルターを手動で実装します:
手順:
- 適切なサイズのビット配列がデータを保持します
- いくつかの異なるハッシュ関数。
- データをビット配列に追加する実装(ブルームフィルター)
- 要素データのビット配列の有無を判定する実装(ブルームフィルター)
import java.util.BitSet;
public class MyBloomFilter {
/**
* 位数组的大小
*/
private static final int DEFAULT_SIZE = 2 << 24;
/**
* 通过这个数组可以创建 6 个不同的哈希函数
*/
private static final int[] SEEDS = new int[]{3, 13, 46, 71, 91, 134};
/**
* 位数组。数组中的元素只能是 0 或者 1
*/
private BitSet bits = new BitSet(DEFAULT_SIZE);
/**
* 存放包含 hash 函数的类的数组
*/
private SimpleHash[] func = new SimpleHash[SEEDS.length];
/**
* 初始化多个包含 hash 函数的类的数组,每个类中的 hash 函数都不一样
*/
public MyBloomFilter() {
// 初始化多个不同的 Hash 函数
for (int i = 0; i < SEEDS.length; i++) {
func[i] = new SimpleHash(DEFAULT_SIZE, SEEDS[i]);
}
}
/**
* 添加元素到位数组
*/
public void add(Object value) {
for (SimpleHash f : func) {
bits.set(f.hash(value), true);
}
}
/**
* 判断指定元素是否存在于位数组
*/
public boolean contains(Object value) {
boolean ret = true;
for (SimpleHash f : func) {
ret = ret && bits.get(f.hash(value));
}
return ret;
}
/**
* 静态内部类。用于 hash 操作!
*/
public static class SimpleHash {
private int cap;
private int seed;
public SimpleHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
/**
* 计算 hash 值
*/
public int hash(Object value) {
int h;
return (value == null) ? 0 : Math.abs(seed * (cap - 1) & ((h = value.hashCode()) ^ (h >>> 16)));
}
}
}
テスト:
String value1 = "https://javaguide.cn/";
String value2 = "https://github.com/Snailclimb";
MyBloomFilter filter = new MyBloomFilter();
System.out.println(filter.contains(value1));
System.out.println(filter.contains(value2));
filter.add(value1);
filter.add(value2);
System.out.println(filter.contains(value1));
System.out.println(filter.contains(value2));
出力:
false
false
true
true
5. GoogleのオープンソースGuavaに付属するBloomフィルターを使用します。
実際のプロジェクトでは、ブルームフィルターを自分で実装しないでください。グアバでのブルームフィルターの実装は、信頼できると見なされています。
まず、プロジェクトに依存関係を反映します
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
実際の使用法は次のとおりです
。1500までの整数を格納できるブルームフィルターを作成しました。誤検出を許容できる確率は0.01%(0.01)です。
// 创建布隆过滤器对象
BloomFilter<Integer> filter = BloomFilter.create(
Funnels.integerFunnel(),
1500,
0.01);
// 判断指定元素是否存在
System.out.println(filter.mightContain(1));
System.out.println(filter.mightContain(2));
// 将元素添加进布隆过滤器
filter.put(1);
filter.put(2);
System.out.println(filter.mightContain(1));
System.out.println(filter.mightContain(2));
この例では、mightContain()メソッドがtrueを返す場合、要素がフィルターにあることを99%確認でき、フィルターがfalseを返す場合、要素がフィルターに存在しないことを100%確認できます。
Guavaが提供するブルームフィルターの実装は依然として非常に優れています(詳細については、ソースコードの実装を確認できます)が、1台のマシンでしか使用できないという大きな欠点があります(さらに、容量の拡張は簡単ではありません)。現在、インターネットは一般的に分散しています。この問題を解決するには、Redisでブルームフィルターを使用する必要があります。
6. Redisのブルームフィルター
- はじめに:
Redis v4.0 以降、モジュール(モジュール/プラグイン)機能があります。Redisモジュールを使用すると、Redisは外部モジュールを使用してその機能を拡張できます。ブルームフィルターはモジュールです。詳細については、RedisモジュールのRedis公式紹介を確認できます:https://redis.io/modules。
さらに、公式Webサイトでは、Redis BloomフィルターのモジュールとしてRedisBloomを推奨しています(アドレス:https : //github.com/RedisBloom/RedisBloom)。その他は次のとおりです。
redis-lua-scaling-bloom-filter(luaスクリプト実装):https : //github.com/erikdubbelboer/redis-lua-scaling-bloom-filter
pyreBloom(Pythonの高速Redis ブルームフィルター):https:/ /github.com/seomoz/pyreBloomは
...
PythonやJavaの、JavaScriptとPHP:RedisBloomには、多言語クライアントのサポートを提供します。
- Dockerを使用したインストール
Redisのブルームフィルターを体験する必要がある場合は、Dockerを使用するだけです。私たちは、その後、直接私たちは答えを見つけたいの広告除外するための最初の検索要素の結果(これは、私は通常、問題を解決する一つの方法、共有している)は、Googleで特定のアドレスは、Redisのブルームフィルタドッカー検索:HTTPSを:// hub.docker.com/r/redislabs/rebloom /(紹介は非常に詳細です)。
具体的な操作は次のとおりです。
➜ ~ docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest
➜ ~ docker exec -it redis-redisbloom bash
root@21396d02c252:/data# redis-cli
127.0.0.1:6379>
-
一般的なコマンドのリスト
注:キー:ブルームフィルターの名前、アイテム:追加された要素。-
BF.ADD:ブルームフィルターに要素を追加します。フィルターがまだ存在しない場合は、フィルターを作成します。形式:BF.ADD {キー} {アイテム}。
-
BF.MADD:「ブルームフィルター」に1つ以上の要素を追加し、まだ存在しないフィルターを作成します。このコマンドの操作モードは、複数の入力を許可し、複数の値を返すことを除いて、BF.ADDと同じです。形式:BF.MADD {キー} {アイテム} [アイテム…]。
-
** BF.EXISTS **:要素がブルームフィルターに存在するかどうかを確認します。形式:BF.EXISTS {key} {item}。
-
BF.MEXISTS:ブルームフィルター形式で1つ以上の要素が存在するかどうかを確認します:BF.MEXISTS {key} {item} [item…]。
-
さらに、BF.RESERVEコマンドを個別に導入する必要があり
ます。このコマンドの形式は次のとおりです:
BF.RESERVE {key} {error_rate} {capacity} [EXPANSION Expansion]。
次に、各パラメータの具体的な意味を簡単に紹介します。- キー:ブルームフィルターの名前
- error_rate:誤検知の予想確率。これは0から1の間の10進数値でなければなりません。たとえば、予想される誤警報率が0.1%(1000分の1)の場合、error_rateは0.001に設定する必要があります。この数がゼロに近いほど、各アイテムのメモリ消費量が多くなり、各操作のCPU使用率が高くなります。
- 容量:フィルターの容量。保存されている要素の実際の数がこの値を超えると、パフォーマンスが低下し始めます。実際の劣化は、制限を超えた程度によって異なります。フィルター要素の数が指数関数的に増加すると、パフォーマンスは直線的に低下します。
オプションのパラメーター: - 拡張:新しいサブフィルターが作成される場合、そのサイズは、現在のフィルターサイズに拡張を掛けたものになります。デフォルトの拡張値は2です。つまり、後続の各サブフィルターは、前のサブフィルターの2倍になります。
-
6.4実際の使用
127.0.0.1:6379> BF.ADD myFilter java
(integer) 1
127.0.0.1:6379> BF.ADD myFilter javaguide
(integer) 1
127.0.0.1:6379> BF.EXISTS myFilter java
(integer) 1
127.0.0.1:6379> BF.EXISTS myFilter javaguide
(integer) 1
127.0.0.1:6379> BF.EXISTS myFilter github
(integer) 0
参照:JavaGuideはそれほど変わっていません
詳細:Deng Xin