詳細ブルーム、ほとんどのアルゴリズム
この記事のソースアドレス:のhttp://www.fullstackyang.com / ...、ありがとう、アドレスまたは転送アドレスsegmentfaultを明記してください!
1.背景
そこブルームフィルタについて多くのオンラインれて、ここでそれらを繰り返すしていない、単純に次の点を抽出します。
- ブルームフィルタは、などの特定のセットが発生し、高速で、少ないスペースバイHaxiテーブル内の要素の重要なツールかどうかを判断するために使用されるが、その中に認識エラー率(偽陽性、偽陽性をいくつかの欠点を持っています)、即ち、確率は非常に小さいが、これは、本番環境の多くで許容可能である判断されたセット内に存在する要素のセットに置くことは不可能です。
- 原理は、図示のように、比較的簡単で、集合Sが有するN k個のハッシュ関数を用いて、図中の要素は、Sの各要素は、mビットの長さ(ビット)異なるアレイ位置Bにマッピングされます要素がk個のハッシュ関数のマッピングを介して検出される場合、バイナリ数の位置は、1に設定され、上の全ての位置K 1バイナリ数は、これは、集合S内の特定の要素ではありません逆に、要素がS(基準1)の一個の要素であってもよいです。
- 、最終的にハッシュ関数の数が必要なことは、要約の説明において、どのようにビット列の長さを作成するには、kの値を推定するために、適切であるとブルームフィルタを構築するとき、Mは、であり、2つのパラメータを渡す必要があり、すなわちFPPに許容される偽陽性率と要素の総数N(正確ではないかもしれません)。パラメータ推定の方法としては、興味のある学生は、直接与えられた式の下に、英語のwikiページを参照することができます:
- その正義の流産の可能性を低減するだけでなく、スペースビット列をフルに活用することができるだけでなく、均等に分散ハッシュ関数の要件を満たすようにしてください。
- 紙:2つのハッシュ関数を提案したトリック「フィルタより良いブルーム構築同じ性能少ないハッシュ」は、k個のハッシュ関数をシミュレートするために使用することができる、すなわち、GI(X)= H1(X)+ IH2(X)ここで、0 <= iが<= K-1。
- 「数学的な美しさ」と博士は呉帳は異なる状況下で、偽陽性率を示し、例えば、16ビット、8つのハッシュ関数を持つ要素は、その後、偽陽性の確率が持っている10000分の5、であると仮定非常に小さいです。
すでにGoogleのグアバライブラリ、TwitterのAlgebirdライブラリ、およびScalaNLPのそよ風など、11.0バージョンは、ブルームフィルタクラスを追加しグアバ、ファンネルとシンクのデザインを使用して、強化されたとして、対応するオープンソースのライブラリが実装されています汎化能力は、それがハッシュ関数マッピングを使用murmur3ハッシュを実行し、データの任意のタイプをサポートすることができ、それは、従来のボトムを使用java.util.BitSetビット列を行うのではなく、長いアレイとこのような柔軟性などの要因をデカップリングするために最終的に我々は、グアバライブラリブルームフィルタは、ソースコードで実装以下の詳細な分析を行い、そして、再パッケージ、業務のほとんどはビットベースの操作されている、非常に優れた性能を達成することが可能です実際の量よりもはるかに大きい場合、我々はJVMからブルームをフィルタリングしたい、検討し、その後、基礎となる再構築されたビット列として利用ビットマップのRedisは、追加的な要素より多くを挿入して、来ます従って、再構成の過程において自動膨張特性を増加させる、ブルームフィルタがますます高くなると予想偽陽性の数を作成する際に、最後通告を設定します その正しさを検証するためにテストされています。
グアバで実装2ブルームフィルタ
グアバ、主に二つのクラス、ブルームフィルタとBloomFilterStrategies、初見のブルームフィルタに関連した実装ブルームフィルタ:
/** The bit set of the BloomFilter (not necessarily power of 2!) */
private final BitArray bits;
/** Number of hashes per element */
private final int numHashFunctions;
/** The funnel to translate Ts to bytes */
private final Funnel<? super T> funnel;
/**
* The strategy we employ to map an element T to {@code numHashFunctions} bit indexes.
*/
private final Strategy strategy;
これは、その4つのメンバ変数であります:
- 詳細は後述する基礎となるオペレーティング・アレイのビットブルームフィルタをカプセル化して内部クラスで定義されBitArraysのBloomFilterStrategies。
- numHashFunctionsは、ハッシュ関数、すなわち、上記kの数を表します。
- グアバで定義されたインターフェースである漏斗は、それがデフォルトjava.nioの持つJava基本データ型(例えば文字、バイト、INT ......などのプリミティブ値)にメインデータの任意のタイプの使用を支持PrimitiveSink。実施のByteBuffer、最終バイト配列に変換されます。
- 戦略は、PUT(インサート要素)、mightContain(要素がいるか否かが判断)及び序方法の三つの方法があり、ブルームフィルタクラスインターフェースは、次のコード内で定義されている(当然のことながら、そのデフォルトのクラス列挙法等)
interface Strategy extends java.io.Serializable {
/**
* Sets {@code numHashFunctions} bits of the given bit array, by hashing a user element.
*
* <p>Returns whether any bits changed as a result of this operation.
*/
<T> boolean put(T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits);
/**
* Queries {@code numHashFunctions} bits of the given bit array, by hashing a user element;
* returns {@code true} if and only if all selected bits are set.
*/
<T> boolean mightContain(
T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits);
/**
* Identifier used to encode this strategy, when marshalled as part of a BloomFilter. Only
* values in the [-128, 127] range are valid for the compact serial form. Non-negative values
* are reserved for enums defined in BloomFilterStrategies; negative values are reserved for any
* custom, stateful strategy we may define (e.g. any kind of strategy that would depend on user
* input).
*/
int ordinal();
}
ブルームpublicコンストラクタ、ブルームフィルタを作成しないためのフィルタ、唯一のプライベートコンストラクタが、それはメソッドを作成し、オーバーロード5を提供しています外で、デフォルトはBloomFilterStrategiesを使用して、3%の偽陽性率に設定されています実現.MURMUR128_MITZ_64。4つの同じメソッドは、最終的にプライベートコンストラクタを呼び出すを担当するメソッドを作成して呼び出して作成し、次のようにそのソースコードは次のとおりです。
static <T> BloomFilter<T> create(
Funnel<? super T> funnel, long expectedInsertions, double fpp, Strategy strategy) {
checkNotNull(funnel);
checkArgument(
expectedInsertions >= 0, "Expected insertions (%s) must be >= 0", expectedInsertions);
checkArgument(fpp > 0.0, "False positive probability (%s) must be > 0.0", fpp);
checkArgument(fpp < 1.0, "False positive probability (%s) must be < 1.0", fpp);
checkNotNull(strategy);
if (expectedInsertions == 0) {
expectedInsertions = 1;
}
/*
* TODO(user): Put a warning in the javadoc about tiny fpp values, since the resulting size
* is proportional to -log(p), but there is not much of a point after all, e.g.
* optimalM(1000, 0.0000000000000001) = 76680 which is less than 10kb. Who cares!
*/
long numBits = optimalNumOfBits(expectedInsertions, fpp);
int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);
try {
return new BloomFilter<T>(new BitArray(numBits), numHashFunctions, funnel, strategy);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Could not create BloomFilter of " + numBits + " bits", e);
}
}
四つのパラメータを作成し、漏斗上で(入力データ)、expectedInsertions(挿入された要素の推定総数)、FPP(予想される偽陽性率)、戦略(戦略の実装例)を受け、そしてそれはアレイのビット長を計算し、そしてハッシュ関数の数(前の式を参照)、そして最後にBitArrayを作成numBits個とし、割り当てを完了するためにコンストラクタを呼び出します。
static long optimalNumOfBits(long n, double p) {
if (p == 0) {
p = Double.MIN_VALUE;
}
return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
}
static int optimalNumOfHashFunctions(long n, long m) {
// (m / n) * log(2), but avoid truncation due to division!
return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
}
続いBloomFilterStrategiesは第二に、それは、それぞれ32ビットのハッシュ関数のマッピングに対応する2つの列挙値2、MURMUR128_MITZ_32及びMURMUR128_MITZ_64を有する。BloomFilter.Strategy列挙クラスインターフェースを達成することである最初のそのクラスを見て、そして64ハより多くのスペースで、すべて128 murmur3ハッシュ生成を使用していますが、原理は同じであるギリシャのマッピング機能は、我々が分析するために、デフォルトのMURMUR128_MITZ_64を選択します。
MURMUR128_MITZ_64() {
@Override
public <T> boolean put(
T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits) {
long bitSize = bits.bitSize();
byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal();
long hash1 = lowerEight(bytes);
long hash2 = upperEight(bytes);
boolean bitsChanged = false;
long combinedHash = hash1;
for (int i = 0; i < numHashFunctions; i++) {
// Make the combined hash positive and indexable
bitsChanged |= bits.set((combinedHash & Long.MAX_VALUE) % bitSize);
combinedHash += hash2;
}
return bitsChanged;
}
@Override
public <T> boolean mightContain(
T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits) {
long bitSize = bits.bitSize();
byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal();
long hash1 = lowerEight(bytes);
long hash2 = upperEight(bytes);
long combinedHash = hash1;
for (int i = 0; i < numHashFunctions; i++) {
// Make the combined hash positive and indexable
if (!bits.get((combinedHash & Long.MAX_VALUE) % bitSize)) {
return false;
}
combinedHash += hash2;
}
return true;
}
抽象ビュー、PUTが書き込まれ、mightContainを読み出し、コードは、2つの方法に少し似ている、第一の入力漏斗を算出バイトの128ビットアレイのmurmur3ハッシュを使用して得られる、採取し、8バイト(64のレベル二つの長整数型のHASH1、ハッシュ値としてHASH2を作成するためのビット)。ループ本体が着想上の2つのアナログ機能の他の機能を使用する上記GI(X)= H1(X)+ IH2(X)、HASH2毎に蓄積に対応する、モジュロベースのビットサイズとしてインデックスビット配列。
プログラミング言語の違いがあり、なぜなら除数および被除数シンボル矛盾するケースの計算で得られた結果をここではLong.MAX_VALUEとビット演算の理由は、「%」は、正確に、モジュロ演算であります(C、C ++、およびJavaが真である、Pythonはモジュロである)5%3 = -2として、及び数学的に定義されているXモジュロ
ので、MOD Y XY = [X / Y](ダウン) - = MOD 3. 5。
-5-3 *( - 2)= 1、ハッシュ値が負の場合の余りを取るその結果が否定的であるように(常にビットサイズ正の数)は、ビット配列で取ることは容易ではありませんこうしてはLong.MAX_VALUE(バイナリ0111 ... 1111)のによって、直接的に除去する先頭に符号ビットに値を、遷移が正の数であるように。あなたはもちろんMURMUR128_MITZ_32がした別の実装では、絶対値を取ることができます。
二重挿入が全く成功を示さず、そしてmightContain取らインデックス位置の値がメソッドを吸引すると、プロセスの最初のインデックス位置1にバイナリカウンタを入れ、その後bitsChanged記録と結果を挿入し、それが真を返し、0か否かを判断します、限り0の出現として、直ちに非存在であることが決定。
次のように最後に、あなたの基本的な実装ビット列を伝えるために、メインのコードは次のようになります。
static final class BitArray {
final long[] data;
long bitCount;
BitArray(long bits) {
this(new long[Ints.checkedCast(LongMath.divide(bits, 64, RoundingMode.CEILING))]);
}
// Used by serialization
BitArray(long[] data) {
checkArgument(data.length > 0, "data length is zero!");
this.data = data;
long bitCount = 0;
for (long value : data) {
bitCount += Long.bitCount(value);
}
this.bitCount = bitCount;
}
/** Returns true if the bit changed value. */
boolean set(long index) {
if (!get(index)) {
data[(int) (index >>> 6)] |= (1L << index);
bitCount++;
return true;
}
return false;
}
boolean get(long index) {
return (data[(int) (index >>> 6)] & (1L << index)) != 0;
}
/** Number of bits */
long bitSize() {
return (long) data.length * Long.SIZE;
}
...
}
グアバもjava.util.BitSetを用いない前に述べたように、むしろ長い型の配列をカプセル化し、長整数型に加えて、第一、(1に設定)占有アレイの数をカウントするために使用されていますあなたは64桁の長配列にいくつかのグループによってスプライシングを想像できるように、セグメント64の長さ(例えば、3つのセクション129に分割)に係る配列の長さの段階の数を長整数型を通過コンストラクタ、その長さは、バイナリコードに対応した1の数をカウントする第コンストラクタLong.bitCount法を用いて、この方法ではJDK1.5は、セグメントの数、すなわちビットサイズを乗じ64があるが、アルゴリズムが設計されています非常に微妙な、学生が自分で勉強するエネルギーを持つことができます。
他の二つの重要なメソッドが設定されていると、その過程で取得し、mightContainの参照方法を入れて、インデックスパラメータ剰余ビットサイズを通過させますので、取得するためには、長い間のこの配列の範囲に入ることができるようになりますインデックス位置に対応するインデックス値は、それが、それは段階の数、セグメント上に得られた値に変換される第一の符号なし右シフト6と丸めダウン動作で割っ64に相当するint型、にキャストされ次に、左結果が0に等しい場合、最後の操作のインデックスは、ビット単位で行わ回し、それはmightContainが不在であるかを決定するために、falseを返します。設定方法において、最初の呼び出しは、同じロジックで抽出したインデックス位置に対応するデータアレイの値、その後ビット単位割り当てまたはバック、存在しない場合に、があったかどうかを決定するための方法を得ます。
ここでは、グアバブルームフィルタは、基本的な議論に実装されている上が終わって、簡潔に要約:
- パラメータを推定するための式を使用して、受信機入力は、最終的な初期化戦略インターフェイスインスタンスを完了するブルームフィルタ・アクション・クラス。
- BloomFilterStrategiesは、二つの部材戦略インターフェース実装を持つ、列挙型であり、それぞれMURMUR128_MITZ_32 MURMUR128_MITZ_64、さらにコアはgetおよびsetメソッドを完了する請求下地ビットブルームフィルタ配列としてlong型の配列をカプセル化ビット・コンピューティング。
3. Redisのビットマップ再構築
上記の分析を通じて、主演算ロジック部は、実際の必要性から、それ自体のためにRedisのためグアバlong型アレイ中に封入された、ビットの基礎となるグループの再構築部分を達成することである、と実質的に同じです「データ構造」(または本質的に列)を持つビットマップは界面位置の操作のために提供されているので、再構成自体は、複雑ではない前述の比較的複雑な、自動膨張特性です。
ここでは自動拡張のアイデアを実現することは、その使用は、新しいキーを生成する一方で、現在のキーに対応するビットマップは、飽和点、カーソルの増加に達した場合、Redisのレコードカーソルのカーソルの増分で、ビットマップは同じスケールを作成します。そして、GET要素はビットマップのいずれかに存在するかどうかを判断する必要があります。要素は、ビットマップ内の現在のカーソルに対応するキーを挿入するために、各ビットマップに存在しないときに全体が、論理なります。
ここでは、コードの一部です。
まず、Redisの動作を簡単にするために、我々はそれぞれ二つのインターフェース関数を定義し、単一のコマンドパイプラインを実行するために、また、簡単なツールを実装
@FunctionalInterface
public interface JedisExecutor<T> {
T execute(Jedis jedis);
}
@FunctionalInterface
public interface PipelineExecutor {
void load(Pipeline pipeline);
}
public class JedisUtils {
private static final GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
private JedisPool jedisPool;
public JedisUtils() {
jedisPool = new JedisPool(poolConfig, "localhost", 6379);
}
public <T> T execute(JedisExecutor<T> jedisExecutor) {
try (Jedis jedis = jedisPool.getResource()) {
return jedisExecutor.execute(jedis);
}
}
public List<Object> pipeline(List<PipelineExecutor> pipelineExecutors) {
try (Jedis jedis = jedisPool.getResource()) {
Pipeline pipeline = jedis.pipelined();
for (PipelineExecutor executor : pipelineExecutors)
executor.load(pipeline);
return pipeline.syncAndReturnAll();
}
}
}
第二の戦略は、行われた変更は、配置すると、コメントの一部であるmightContainは、グアバで達成されます。簡単にするために、ここで我々は唯一のStringオブジェクトを受け入れます。
ここで、コレクタ・アレイへの最初のインデックス位置に対応する全てのランダム関数、処理はRedisBitmaps底又はSET取得する称される、特定のプロセスの詳細については後述します。
bits.ensureCapacityInternal()メソッドは、すなわち、自動拡張機能名がArrayListの上から搬送されていることを示しています。
@Override
public boolean put(String string, int numHashFunctions, RedisBitmaps bits) {
long bitSize = bits.bitSize();
byte[] bytes = Hashing.murmur3_128().hashString(string, Charsets.UTF_8).asBytes();
long hash1 = lowerEight(bytes);
long hash2 = upperEight(bytes);
boolean bitsChanged = false;
long combinedHash = hash1;
// for (int i = 0; i < numHashFunctions; i++) {
// bitsChanged |= bits.set((combinedHash & Long.MAX_VALUE) % bitSize);
// combinedHash += hash2;
// }
long[] offsets = new long[numHashFunctions];
for (int i = 0; i < numHashFunctions; i++) {
offsets[i] = (combinedHash & Long.MAX_VALUE) % bitSize;
combinedHash += hash2;
}
bitsChanged = bits.set(offsets);
bits.ensureCapacityInternal();//自动扩容
return bitsChanged;
}
@Override
public boolean mightContain(String object, int numHashFunctions, RedisBitmaps bits) {
long bitSize = bits.bitSize();
byte[] bytes = Hashing.murmur3_128().hashString(object, Charsets.UTF_8).asBytes();
long hash1 = lowerEight(bytes);
long hash2 = upperEight(bytes);
long combinedHash = hash1;
// for (int i = 0; i < numHashFunctions; i++) {
// if (!bits.get((combinedHash & Long.MAX_VALUE) % bitSize)) {
// return false;
// }
// combinedHash += hash2;
// }
// return true;
long[] offsets = new long[numHashFunctions];
for (int i = 0; i < numHashFunctions; i++) {
offsets[i] = (combinedHash & Long.MAX_VALUE) % bitSize;
combinedHash += hash2;
}
return bits.get(offsets);
}
最後に、そして最も重要なRedisBitmaps、請求setbitビットサイズグアバロングブルームフィルタ配列の長さはビットサイズを与えるように計算され、以下の方法により0に設定されたすべてのビットを初期化するコマンド。各オフセットするために、すべての、グアバブルームとの両方が、同様のロジックをフィルタリングここではそれらを繰り返さない、と(ロング[]オフセット)メソッドを取得し、(長いオフセット)を取得(ロングオフセット)と設定ビットマップの判定に対応するカーソルは、すべてのヒットした場合、この要素がビットマップ中に存在すること、そして完全にヒットしない場合はその逆でもよく、要素は、任意のビットマップに存在しないことを示すので、この条件が満たされ、セット(長いです[ ] Qoffsets)メソッドは、現在のビットマップのキーに挿入することができます。
ensureCapacityInternal法、決意条件が必要とされる膨張BITCOUNT * 2>ビットサイズでは、BITCOUNTビットマップは、「1」を表し、「1」の数が全体の半分以上、拡大のための操作で表示されたとき、つまり、数を表示 - INCRコマンドカーソル増分を使用する最初は、その後、新しいビットマップを作成するために、新しいキーを使用します。
RedisBitmapsJava
クラスRedisBitmaps {
private static final String BASE_KEY = "bloomfilter";
private static final String CURSOR = "cursor";
private JedisUtils jedisUtils;
private long bitSize;
RedisBitmaps(long bits) {
this.jedisUtils = new JedisUtils();
this.bitSize = LongMath.divide(bits, 64, RoundingMode.CEILING) * Long.SIZE;//位数组的长度,相当于n个long的长度
if (bitCount() == 0) {
jedisUtils.execute((jedis -> jedis.setbit(currentKey(), bitSize - 1, false)));
}
}
ブールGET(ロング[] Qoffsets){
ため(= 0ロングI; Iは、カーソル()+は<1; Iは++){
最終ロングカーソル= I;
//長いビットマップに対応するカーソルがあるので、次に、すべてのヒットをQoffsetsあってもよい
ブールマッチ= Arrays.stream(Qoffsets).boxed()
.MAP(オフセット- > jedisUtils.execute(jedis - > jedis.getbit(でgenkey(カーソル)))オフセット)
.allMatch(B - >(ブール) B);
IF(一致)
真への復帰;
}
偽に戻ります;
}
boolean get(final long offset) {
return jedisUtils.execute(jedis -> jedis.getbit(currentKey(), offset));
}
boolean set(long[] offsets) {
if (cursor() > 0 && get(offsets)) {
return false;
}
boolean bitsChanged = false;
for (long offset : offsets)
bitsChanged |= set(offset);
return bitsChanged;
}
boolean set(long offset) {
if (!get(offset)) {
jedisUtils.execute(jedis -> jedis.setbit(currentKey(), offset, true));
return true;
}
return false;
}
long bitCount() {
return jedisUtils.execute(jedis -> jedis.bitcount(currentKey()));
}
long bitSize() {
return this.bitSize;
}
private String currentKey() {
return genkey(cursor());
}
private String genkey(long cursor) {
return BASE_KEY + "-" + cursor;
}
private Long cursor() {
String cursor = jedisUtils.execute(jedis -> jedis.get(CURSOR));
return cursor == null ? 0 : Longs.tryParse(cursor);
}
void ensureCapacityInternal() {
if (bitCount() * 2 > bitSize())
grow();
}
void grow() {
Long cursor = jedisUtils.execute(jedis -> jedis.incr(CURSOR));
jedisUtils.execute((jedis -> jedis.setbit(genkey(cursor), bitSize - 1, false)));
}
void reset() {
String[] keys = LongStream.range(0, cursor() + 1).boxed().map(this::genkey).toArray(String[]::new);
jedisUtils.execute(jedis -> jedis.del(keys));
jedisUtils.execute(jedis -> jedis.set(CURSOR, "0"));
jedisUtils.execute(jedis -> jedis.setbit(currentKey(), bitSize - 1, false));
}
private PipelineExecutor apply(PipelineExecutor executor) {
return executor;
}
}
のは、その正しさを検証するためのユニットテストをやってみましょう。
我々は1の元の予想、RedisBloomFilter拡張の合計数と同じ数を挿入すると、2つのブルームフィルタと一致した結果は、偽真、偽です。
番号はまだ元の3回の合計、RedisBloomFilter膨張三回を挿入し、期待されている場合、正しく判断すること、及び誤判定が発生したときにグアバSTR3ブルームフィルタが決定されます。
public class TestRedisBloomFilter {
private static final int TOTAL = 10000;
private static final double FPP = 0.0005;
@Test
public void test() {
RedisBloomFilter redisBloomFilter = RedisBloomFilter.create(TOTAL, FPP);
redisBloomFilter.resetBitmap();
BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), TOTAL, FPP);
IntStream.range(0, /* 3* */TOTAL).boxed()
.map(i -> Hashing.md5().hashInt(i).toString())
.collect(toList()).forEach(s -> {
redisBloomFilter.put(s);
bloomFilter.put(s);
});
String str1 = Hashing.md5().hashInt(99999).toString();
String str2 = Hashing.md5().hashInt(9999).toString();
String str3 = "abcdefghijklmnopqrstuvwxyz123456";
System.out.println(redisBloomFilter.mightContain(str1) + ":" + bloomFilter.mightContain(str1));
System.out.println(redisBloomFilter.mightContain(str2) + ":" + bloomFilter.mightContain(str2));
System.out.println(redisBloomFilter.mightContain(str3) + ":" + bloomFilter.mightContain(str3));
}
}
>>
grow bloomfilter-1
false:false
true:true
false:false
>>
grow bloomfilter-1
grow bloomfilter-2
grow bloomfilter-3
false:false
true:true
false:true
要約すると、我々はグアバは、ブルームフィルタを考えて使用して、結合特性などのビットマップはRedisのは、動的な拡張ブルームフィルタをサポートして達成され、それはそう、データベースのRedisへの根本的なビットブルームフィルタのデータをロードします利点は、それが、より複雑なマルチアプリケーションや分散システム内に配備されてもよく、また、Redisの持続性、期限切れタイマー機能を完了するために利用することができるされています。