記事のディレクトリ
前
私たちは、Redisの高度な-Redisキャッシュの最適化はキャッシュの侵入を防ぐための解決策について話しました:ブルームフィルタを解決するためのより良い方法の値よりもキャッシュを空に、ここでは、以下に詳細に説明します。
ブルームは、どのような問題を解決することができますか?
例:あなたが100,000の電話番号は現在、50億個の電話番号があり、どのように迅速かつ正確にこれらの番号の有無を決定するには?
スキームA:DB?----> 50億電話番号、このクエリの効率?
プログラムB:メモリ?- >電話番号8をクリックしてバイト5,000,000,000バイト* 8 = 40Gメモリ...
プログラムC:hyperloglog ---->精度少し低いですか?
など、多くの同様の問題があり、
- スパムフィルタリング
- ワープロソフト(Wordなどの)ワードのエラー検出
- ウェブクローラ重複URL検出
- HBaseのラインフィルタ
- ...
原則ブルームフィルタ
問題を解決するために非常に小さなスペースでバートン。ブルーム、によって提案された1970
長いバイナリーベクター、プラスハッシュ関数の数(それは超巨大なアレイの基礎となるデータ構造であることが理解されるであろうのみ0と1の続き)
1が存在しない場合、我々は、対応する位置に、各セットのハッシュ結果の計算、k番目の計算のためのk個のハッシュ関数を有し、そして再度再計算するときハッシュ関数取得ブルームフィルタであります数は、すべての1つだけが存在していたが、存在しません。
預金や計算方法はそれ以外の場合は、Xiecai同じでなければなりません。。。。
建設ブルームフィルタ
パラメータ:Mバイナリーベクター、N予備データ、k個のハッシュ関数
建設ブルームフィルタ:nは、再び上記の手順を行って準備ができ番目のデータ
要素の存在または不在を決定する:1両方の場合に再テイク(K倍ハッシュ関数)に再びこのデータ、ビルドプロセスを、それが存在、非存在およびその逆を示します。
エラーレートブルームの構築
当初は、ブルームフィルタを使用して、可能性のあるエラーの存在をエラーを受け入れなければなりません。確かにちょうどすべてのヒットでエラーがあります
例えば、k個のハッシュ後の二つの値があり、値が1である、実際には、この時間を算出し、あなたの唯一の根本的な配列の値、およびブルームは、別の値があるあなたを伝えます
パラメータ:Mバイナリーベクター、N予備データ、k個のハッシュ関数
:直感的な要因M / N、ハッシュ関数の数の比で
(例えば1000と、あなたに提供されるマッピング関係を格納するための小さなバイナリベクトルmと仮定する、唯一1000年ストレージ1000は言わないが提供される、例えばグアバのデータ計算の基礎となるグアバ多数の、あなたのセットと併せて誤り率をスーパーアレイの長さ)、nを(データ量)とスーパーマルチ、例えば100万ドルを計算し、計算するために使用される三つのハッシュ関数が存在します。
今回は、データ「職人」を持っています
第1のハッシュ関数演算は、5つの要素の元の配列の位置に格納された後に
第2のハッシュ関数の計算を介して100個の要素の元の配列の位置に格納されている
元の配列に格納された第3のハッシュ関数演算上1024個の要素の位置
位置の値に対応する第51001024要素は、それが存在すること、1であるとして、あなたが存在または不在業者を決定する必要があり、この時間は、唯一限り三個のハッシュ関数を再計算する必要があります。限り、0があるとして、それは存在しません。
彼は最初の51001024個の要素の位置を上陸させたことが起こった場合、私はブルームはあなたが現実に存在するxxx部分語ったことを、3つのハッシュ計算した後、データXXXXの別の部分があると?実際、最初の51001024この値は不正確なデータをもたらした、というよりも、xxxは、職人から計算され、エラーの可能性を受け入れなければなりません。
M / Nの誤り率に反比例する、kはエラー率に反比例します
M / N誤差に反比例する:あなたが店Mバイナリアレイを大きくするには、Mバイナリーベクター、N-レディデータ、小さいあなたが実際のNを格納するために必要なデータは、その後、M / Nよりも大きいですか?エラーレートが相応に低いこと。
kと、エラー率が反比例する:イェジンハオは、あなたが唯一のハッシュ関数があるとし、このことを理解し、あなたは可能性がはるかに高い繰り返しではないでしょうか?大きなK、低エラーレートがそう。
実際のエラー率の予測
パラメータ:Mバイナリーベクター、N予備データ、k個のハッシュ関数
-
1)要素、ハッシュ関数、任意のビットの確率が1、確率は0 1- 1 / M 1 / Mであります
-
依然として2)k個の関数、k番目のパワーの0(1-1 / M)の確率は、n型素子、NKの0(1-1 / M)乗の確率
-
NKの(1-1 / M)乗 - 3)1に設定され、確率が1であります
-
4)全体の新しい要素の確率
ブルームフィルタ(JVMレベル)
ビットマップは最初に理解することができ、特別なアルゴリズムAlgorithms_ _Bitmapの原理および応用を
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
public class GuavaBloomFilterTest {
// BloomFilter 容量 (实际上Guava通过计算后开辟的空间远远大于capacity)
private static final int capacity = 100000;
// 构建 BloomFilter
private static BloomFilter bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity);
// 模拟初始化数据
static{
for (int i = 0; i < capacity; i++) {
bloomFilter.put(i);
}
}
public static void main(String[] args) {
int testData = 999;
long startTime = System.nanoTime();
if (bloomFilter.mightContain(testData)){
System.out.println(testData + " 包含在布隆过滤器中 ");
}
System.out.println("消耗时间: " + (System.nanoTime() - startTime) + " 微秒");
// 错误率判断
double errNums = 0;
for (int i = capacity + 1000000; i < capacity + 2000000; i++) {
if (bloomFilter.mightContain(i)) {
++errNums;
}
}
System.out.println("错误率: " + (errNums/1000000));
}
}
ローカルブルームフィルタ問題
- 容器の容量は、TomcatのJVMとして、ローカルメモリが限られています
- アプリケーション複数のブルームフィルタは、複数の複雑な同期構造(セッション類推など理解)
- 再構築するアプリケーションキャッシュされたコンテンツのニーズを再起動します。
悪意のある攻撃は、サーバーのキャッシュデータが浸透によるものは存在しないに多数の要求は、第1のブルームフィルタを通してろ過しないことができ、ブルームフィルタデータの要求がダウンして行かせていない、一般的にフィルタリングすることができます存在しません。バックエンドを送信します。
ブルームフィルタ戻り値が存在する場合、値が存在しない場合があります。それは、それは確かにそこに存在していないと言うとき。
ブルームフィルタは、多数のビットが同じグループと、公正なハッシュ関数はありませんです。
公正は、要素のハッシュ値が比較的均一に算出することができると呼ばれます。
キーがブルームフィルタに追加されると、キーハッシュ値にハッシュ関数を複数に使用される、位置を得るために長モジュロ演算のビット数の整数インデックスとみなし、各ハッシュ関数は、異なる場所考えます。次いで、これらのビットの位置を一つのグループが完了した追加操作に設定されています。
その、追加ように、また、いくつかのハッシュの位置はこれらの位置のビットの数があればビットが0であるように、1に設定されているかどうかを確認するために計算され、ブルームフィルタにキーが存在するかどうかを尋ねられたときこのキーでブルームフィルタは存在しません。
あなたが1であれば、これは、このキーが存在しなければならないことを意味するものではありませんが、これらのビットが1に設定されているので、最も可能性があり、他の主要な原因の存在に起因する可能性があります。
このビットスパースグループは、この確率は素晴らしいものだ場合、このビットがグループを混雑している場合、この確率は減少します。
この方法では、高いデータヒット、比較的固定データ、リアルタイム低い(一般的に、より大きなデータセット)アプリケーションシナリオ、より複雑なコードの保守には適していませんが、キャッシュは非常に小さなスペースを占めます。
擬似コード
ブルームフィルタをguvuaできる袋を運ぶ、導入依存性
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
import com.google.common.hash.BloomFilter;
//初始化布隆过滤器
//1000:期望存入的数据个数,0.001:期望的误差率
BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf‐8")), 1000, 0.001);
//把所有数据存入布隆过滤器
void init(){
for (String key: keys) {
bloomFilter.put(key);
}
}
String get(String key) {
// 从布隆过滤器这一级缓存判断下key是否存在
Boolean exist = bloomFilter.mightContain(key);
if(!exist){
return "";
}
// 从缓存中获取数据
String cacheValue = cache.get(key);
// 缓存为空
if (StringUtils.isBlank(cacheValue)) {
// 从存储中获取
String storageValue = storage.get(key);
cache.set(key, storageValue);
// 如果存储数据为空, 需要设置一个过期时间(300秒)
if (storageValue == null) {
cache.expire(key, 60 * 5);
}
return storageValue;
} else {
// 缓存非空
return cacheValue;
}
}
ブルームフィルタ(分散)
私たちは、地元のブルームフィルタ、単一のアプリケーションの欠点を解析し、複数のアプリケーション間での同期が困難ブルームフィルタのみが存在し、アプリケーションの再起動に一度、キャッシュミス。
分散環境の場合は、分散型ブルームフィルタのRedisを構築するために利用することができます
使用redissonのフレームワーク
https://github.com/redisson/redisson/wiki/6.-distributed-objects#68-bloom-filter
RBloomFilter<SomeObject> bloomFilter = redisson.getBloomFilter("sample");
// initialize bloom filter with
// expectedInsertions = 55000000
// falseProbability = 0.03
bloomFilter.tryInit(55000000L, 0.03);
bloomFilter.add(new SomeObject("field1Value", "field2Value"));
bloomFilter.add(new SomeObject("field5Value", "field8Value"));
bloomFilter.contains(new SomeObject("field1Value", "field8Value"));
bloomFilter.count();
ブルームフィルタは、Redisのは、DBを破砕し、DBに落ちる多数の要求で、その結果、シーンのキャッシュの浸透を解決しています。
ブルームフィルタはとてもキャッシュとDB間でのRedis必要があります。
ブルームは存在してはならない、必ずしも存在しないがあることを示しています。ですから、DBから値を発見していないとき、あなたはブルームフィルタには、このキーの更新を置く必要があり、次回このキーとその後、時間をかけて、何の直接的なリターン、再びDBを照会する必要はありません。
擬似コード
public String getByKey(String key) {
String value = get(key);
if (StringUtils.isEmpty(value)) {
logger.info("Redis 没命中 {}", key);
if (bloomFilter.mightContain(key)) {
logger.info("BloomFilter 命中 {}", key);
return value;
} else {
if (mapDB.containsKey(key)) {
logger.info("更新 Key {} 到 Redis", key);
String valDB = mapDB.get(key);
set(key, valDB);
return valDB;
} else {
logger.info("更新 Key {} 到 BloomFilter", key);
bloomFilter.put(key);
return value;
}
}
} else {
logger.info("Redis 命中 {}", key);
return value;
}
}
ブルームフィルタの欠点
唯一の時間と空間の効率化を実現するための判断、利便性、削除の精度を犠牲にしてブルームフィルタは、比較的高く、なぜなら
-
ハッシュ値が1になった後偽決意がコンテナ内の要素ではないに見出すことができるが、k個の位置が得られます。ブルームフィルタは、ブラックリストに格納されている場合は、ホワイトリストの設立を通して、正義の流産かもしれ要素を格納することができます。
-
データを削除。アレイのビット位置にマッピングされた容器Kへの要素が1であり、単に直接0に設定されている場合、他の要素の決意に影響を与える可能性があり、削除することができません。カウントブルームフィルタを考慮することができます