Redisシリーズの記事:
徹底的なRedisシリーズ(1):LinuxでのRedisのインストール
Thorough Redisシリーズ(2):Redisの6つのデータ型の詳細な使用法
See Through Redisシリーズ(3):Redisパイプライン、パブリッシュ/サブスクライブ、モノ、有効期限の詳細な紹介
Redisシリーズ(4)をご覧ください:ブルームフィルターの詳細
徹底的なRedisシリーズ(5):RDBとAOFの永続性の詳細な紹介
Thorough Redisシリーズ(6):マスタースレーブレプリケーションの詳細な紹介
Thorough Redisシリーズ(7):センチネルメカニズムの詳細な紹介
See Through Redisシリーズ(8):クラスターの詳細な紹介
徹底的なRedisシリーズ(9):Redisプロキシtwemproxyとpredixyの詳細な紹介
Thorough Redisシリーズ(10):Redisメモリモデルの詳細な紹介
徹底的なRedisシリーズ(11):ジェダイとレタスのクライアントの詳細な紹介
記事のディレクトリ
このブログでは、主にRedisを使用してブルームフィルターを実装する方法を紹介しますが、ブルームフィルターを紹介する前に、まずブルームフィルターを使用する理由を紹介します。
ブルームフィルターのアプリケーションシナリオ
- キャッシュ侵入の問題を解決する
通常の状況では、最初にキャッシュにデータがあるかどうかを照会し、次にキャッシュにデータがない場合はデータベースに照会します。データがデータベースに存在しない場合、クエリごとにデータベースにアクセスする必要があります。これはキャッシュの浸透です。キャッシュペネトレーションの問題は、データベースに存在しないデータをクエリする要求が多数ある場合、データベースに圧力をかけ、さらにはデータベースをダウンさせることです。
ブルームフィルターは、キャッシュ侵入の問題を解決し、既存のデータをkey
ブルームフィルターに保存するために使用できます。新しいリクエストがある場合は、最初にブルームフィルタに存在するかどうかを確認し、データが存在しない場合は直接返します。データが存在する場合は、キャッシュクエリデータベースにクエリを実行します。
- ブラックリストの検証
ブラックリストにある場合は、特定の操作を実行します。例:スパムを識別するために、メールボックスがブラックリストにある限り、スパムとして識別されます。ブラックリストの数が数億にあると仮定すると、ブラックリストを保存するのに非常に多くのストレージスペースが必要になります。ブルームフィルターの方が優れたソリューションです。すべてのブラックリストをブルームフィルターに入れ、メールを受信したら、そのメールアドレスがブルームフィルターに含まれているかどうかを判断します。
**シナリオ1:**元々は10億の数字でしたが、現在は100,000の数字があります。これらの10万の数字が10億の数字データベースにあるかどうかをすばやく正確に判断するには、
解決策1:データベースに10億の数値を格納し、データベースクエリを実行します。精度は良好ですが、速度は遅くなります。
解決策2:Redisキャッシュなどの10億の数値をメモリに入れます。ここでは、メモリサイズを計算します。10億* 8バイト= 8GB、メモリクエリにより、精度と速度はすべてですが、約8GBです。メモリスペースはメモリスペースの浪費。
**シナリオ2:**ショッピングWebサイトで商品を検索し、顧客が商品検索バーに商品を入力します。まず、商品がデータベースに存在するかどうかを確認する必要があります。存在する場合は、データベースクエリ操作を実行します。実行されます!
そのため、このような大規模なデータコレクションの場合、メモリを占有せずに特定のデータが大規模なデータコレクションに含まれているかどうかを正確かつ迅速に判断する方法として、ブルームフィルターが登場しました。
ブルームフィルター入門
上記の質問で、ブルームフィルターとは正確に何であるかを見てみましょう。
ブルームフィルター:バイナリ配列と見なすことができるバイナリベクトルの長い文字列で構成されるデータ構造。バイナリであるため、0または1のいずれかを格納しますが、初期のデフォルト値は0です。
次のように:
1.データを追加します
コンセプトを紹介するときに、ブルームフィルターはコンテナーと見なすことができると言いましたが、ブルームフィルターにデータを追加するにはどうすればよいですか?
次の図に示すように、ブルームフィルターに要素キーを追加する場合は、複数のハッシュ関数を使用して値を計算し、この値が配置されている正方形を1に設定します。
たとえば、次の図では、hash1(key)= 1の場合、2番目のグリッドで0を1に変更し(配列は0からカウントされます)、hash2(key)= 7、8番目のグリッドを1に設定してから、類推。
2.データが存在するかどうかを判断しますか?
ブルームフィルターにデータを追加する方法を知っているので、このブルームフィルターに新しいデータが存在するかどうかをどのように判断しますか?
非常に単純です。この新しいデータを上記のカスタムハッシュ関数に渡して各値を個別に計算し、対応する場所がすべて1であるかどうかを確認するだけで、1でない状況がある場合は、次のように言うことができます。このブルームフィルターに新しいデータが存在していてはなりません。
一方、ハッシュ関数によって計算された値が対応する場所で1である場合、このデータはこのブルームフィルターに存在する必要がありますか?
ハッシュ関数で計算された複数の異なるデータの結果が繰り返されるため、答えはノーです。ハッシュ関数で他のデータが1に設定される特定の位置があります。
結論を得ることができます。ブルームフィルターは、特定のデータが存在してはならないことを判別できますが、存在している必要があることを判別することはできません。
3.ブルームフィルターの長所と短所
利点:利点は明らかです。バイナリ配列はメモリをほとんど消費せず、挿入とクエリの速度は十分に高速です。
短所:データの増加に伴い、誤判断の割合が増加します。また、データが存在する必要があると判断できないこともあります。また、重要な短所として、データを削除できません。
Redisはブルームフィルターを実装しています
Redisではbitmap
ブルームフィルターが実現されています!
ビットマップ
コンピュータは基礎となるストレージの基本単位としてバイナリビットを使用し、1バイトは8ビットに等しいことを私たちは知っています。
たとえば、「big」文字列は3文字で構成されます。これらの3文字に対応するASCIIコードは98、105、103であり、対応するバイナリストレージは次のとおりです。
Redisでは、ビットマップは、上記と同様の文字列内の各ビットを操作するための一連のコマンドを提供します。
設定
setbit key offset value
127.0.0.1:6379> set k1 big
OK
127.0.0.1:6379> setbit k1 7 1
(integer) 0
127.0.0.1:6379> get k1
"cig"
127.0.0.1:6379>
「b」のバイナリ表現が0110010であることがわかっているので、7番目のビット(0から開始)を1に設定すると、0110 0011は文字「c」を表すため、最後の文字「big」は「cig」になります。
指定された範囲が1であるビットマップの数を取得します
bitcount key [start end]
指定しない場合は、1のすべての数値を取得します。
注:startとendは、ビット配列の添え字ではなく、バイト数を指定します。
127.0.0.1:6379> set k1 big
OK
127.0.0.1:6379> bitcount k1
(integer) 12
127.0.0.1:6379> bitcount k1 0 0
(integer) 3
127.0.0.1:6379> bitcount k1 0 1
(integer) 7
127.0.0.1:6379>
ブルームフィルターモジュールをRedisインストールします
1. githubアドレスにアクセスして、モジュールのソースコードをダウンロードします
https://github.com/RedisBloom/RedisBloom
git cloneを直接使用するか、zipをダウンロードします
git clone https://github.com/RedisBloom/RedisBloom.git
2. makeを実行して、ダイナミックライブラリをコンパイルします
cd RedisBloom
make
実行が完了すると、redisbloom.soダイナミックライブラリが生成されます
3. redisを起動して、ダイナミックライブラリをロードします
# 我习惯把该库放到redis的安装目录下,这步骤看自己喜好
sudo cp redisbloom.so /opt/redis/
# 先停掉redis进程
sudo kill -9 pid
# 加载动态库
redis-server --loadmodule /opt/redis/redisbloom.so
次の図は、ロードが完了したことを示しています。
次に、redis-cli
クライアントを使用して接続およびテストできます
Redisはブルームフィルターを使用します
1.一般的に使用されるコマンド
bf.add要素を追加
bf.existsは、要素が存在するかどうかを照会します
bf.maddは一度に複数の要素を追加します
bf.mexistsは、複数の要素が同時に存在するかどうかを照会します
127.0.0.1:6379> bf.add k1 1
(integer) 1
127.0.0.1:6379> bf.add k1 2
(integer) 1
127.0.0.1:6379> bf.exists k1 1
(integer) 1
127.0.0.1:6379> bf.exists k1 5
(integer) 0
127.0.0.1:6379>
2.ブルームフィルターの正解率
ブルームフィルターの精度を決定するredisには2つの値があります:
error_rate:ブルームフィルターのエラー率を許可します。値が小さいほど、フィルターのビット配列のサイズが大きくなり、占有されるスペースが大きくなります。
initial_size:ブルームフィルターが格納できる要素の数。実際に格納される要素の数がこの値を超えると、フィルターの精度が低下します。
redisには、次の2つの値を設定するコマンドがあります。
bf.reserve test 0.01 100
最初の値はフィルターの名前です。
2番目の値はerror_rateの値です。
3番目の値はinitial_sizeの値です。
追加する前に、bf.reserveコマンドを使用して明示的に作成する必要があることに注意してください。対応するキーがすでに存在する場合、bf.reserveはエラーを報告します。同時に、エラー率が低く設定されるほど、より多くのスペースが必要になります。bf.reserveを使用しない場合、デフォルトのerror_rateは0.01で、デフォルトのinitial_sizeは100です。
3.プロジェクトで使用する
3.1
パッケージのインポート
<dependency>
<groupId>com.redislabs</groupId>
<artifactId>jrebloom</artifactId>
<version>1.0.2</version>
</dependency>
JARパッケージには3つのクラスしかなく、接続メソッドとデータ型のサポートが不十分です。
コード:
Client client = new Client(redisProperties.getHost(), redisProperties.getPort(), 10000, 100);
client.add("bobo", "123");
boolean bo = client.exists("bobo", "123");
System.out.println(bo);
3.2:グアバのBloomFilter
BloomFilterクラスは、サーバーメモリを直接使用するGoogleのguavaパッケージで提供されます
パッケージのインポート
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
コード:
private static int size = 1000000;
private static BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), size, 0.0001);
public void test2() {
String bo = "bobo";
bloomFilter.put(bo);
System.out.println(bloomFilter.mightContain(bo));
}