ビットマップとブルームフィルター
大容量整数に値があるかどうか--bitmap
プログラムでは、特定の数がセットに存在するかどうかを判断できる場合が多く、ほとんどの場合、マップやリストなどの単純なデータ構造を使用するだけで済みます。高級言語を使用する場合、Expressで乗算して、いくつかのパッケージ化されたAPIを呼び出し、elseの場合はいくつか追加することもできます。また、2行または3行のコードで、コンソールで実行されている「完璧」で「堅牢な」コードを確認できます。
ただし、完璧なものはありません。同時実行性の高い環境では、すべてのケースが極端になります。これが非常に大きなコレクション(この膨大な量、1億に特定の値を与える)の場合、リンクリストに関係なく、単純なハッシュマップです。必要なポインタメモリスペース、int型の1億の整数は、380 M(4byte×10 ^ 8)以上を必要とし、10億は4 Gです。パフォーマンスに関係なく、メモリのオーバーヘッドを計算するだけです。地上はすべて128Gサーバーで、このポットは食べられません。
ビットマップはビット数を使用して数値のサイズを表し、ビットに格納されている0または1は、整数が存在するかどうかを識別します。具体的なモデルは次のとおりです。
これは、4つの数字4321が存在する0〜9を識別できる「ビットマップ」です。
ビットマップのメモリオーバーヘッドを計算します。1億以内のデータ検索の場合、大規模なデータ検索を完了するために必要なのは1億ビット=約12MBのメモリスペースだけです。これは非常に魅力的なメモリ削減ですか?以下はビットマップコードです。 Javaで実装:
public class MyBitMap {
private byte[] bytes;
private int initSize;
public MyBitMap(int size) {
if (size <= 0) {
return;
}
initSize = size / (8) + 1;
bytes = new byte[initSize];
}
public void set(int number) {
//相当于对一个数字进行右移动3位,相当于除以8
int index = number >> 3;
//相当于 number % 8 获取到byte[index]的位置
int position = number & 0x07;
//进行|或运算 参加运算的两个对象只要有一个为1,其值为1。
bytes[index] |= 1 << position;
}
public boolean contain(int number) {
int index = number >> 3;
int position = number & 0x07;
return (bytes[index] & (1 << position)) != 0;
}
public static void main(String[] args) {
MyBitMap myBitMap = new MyBitMap(32);
myBitMap.set(30);
myBitMap.set(13);
myBitMap.set(24);
System.out.println(myBitMap.contain(2));
}
}
単純なバイト配列とビット演算を使用して、時間と空間の完璧なバランスを実現できます。それは美しいことではありませんか。これが1億未満のセットであるが、桁違いが10であることが明確になった場合、ビットマップを使用します。これにも12Mのデータが必要です。10億未満のデータの場合、オーバーヘッド120Mに上昇し、ビットマップのスペースオーバーヘッドそれは常に彼のデータの値の範囲にリンクされています。彼は大量のデータでのみ彼のスキルを示すことができます。
今述べた極端なケースについて話しましょう。データ量が1000万、値の範囲が10億以内だとすると、必然的に1億2000万のオーバーヘッドに直面することになります。それに対処する方法はありますか?
ブルームフィルター
著者が述べた上記の問題に直面した場合、ハッシュなどの従来のソリューションを組み合わせて、10億以内の特定のデータを1億以内の値にハッシュしてから、ビットマップに移動して方法を確認します。以下に示すように、ブルームフィルターはまさにそれを行います:
複数のハッシュアルゴリズムによって取得された値を使用して、ハッシュ衝突の可能性を減らします
上記の凡例で述べたように、衝突の可能性を減らすために複数のハッシュアルゴリズムを使用できますが、衝突がある限り、間違った判断が必要です。値が実際に存在するかどうかを100%確信することはできませんが、ハッシュアルゴリズムの魅力は、存在するかどうかはわかりませんが、本当に存在しないかどうかはわかります。そのため、上記の実装は「フィルター」と呼ばれます。
高並行性キャッシュ設計戦略
なぜキャッシュ?
読者がコンピュータサイエンスを専攻している学生である場合、単語キャッシュは耳を繭にすることができる頻度を持っている必要があります。コンピュータシステムでは、キャッシュはCPUとメモリの間のピースメーカーであり、CPUとメモリの処理速度の間のギャップを緩和するために使用されます。OSでは、ページキャッシュはメモリとIOの間のピースメーカーです。(パブリックアカウントのJava懐かしい友人を検索し、「2021」と返信して、Javaインタビューの質問のコレクションを送信します)
キャッシュは平和なものですか??奇妙に聞こえますが、それもかなり印象的です。
アルゴリズム理論のほとんどについては前に説明しましたが、読者が眠くなるのを防ぐために、トピックの後半である高同時実行性キャッシュの設計について直接説明します。
ソフトウェア層でも、最も単純なサービスアーキテクチャから始めて、通常はサーバー側でリクエストを開始し、次にMysqlなどのリレーショナルデータベースをCURDするという、このような安心感が必要です。ただし、このようなアーキテクチャでは、永続化のための端末としてディスクが必要です。インデックスが追加された場合でも、クエリを最適化するためにB +ツリーデータ構造が使用され、頻繁なシークを必要とするIOで効率が維持されます。現時点では、古いものの役割は非常に明白です。IO処理速度の低下によるプレッシャーを軽減するために、いくつかのメモリ操作を追加します。キャッシュは問題ではありませんが、実際にはどのように使用するかが問題になります。
キャッシュコヒーレンシの問題
キャッシュ処理にはいくつかのメカニズムがあります。
- キャッシュはさておき;
- 読み飛ばします;
- 書き抜く;
- キャッシングの背後に書き込む;
キャッシュ侵入の問題
いわゆるキャッシュブレークダウンとは、リクエストが送信されてデータをキャッシュで読み取ることができない場合でも、リクエストがデータベースに影響を与えることを意味します。この場合、キャッシュの解凍の影響はなくなります。
このようなシナリオを想像してみてください。ユーザーが悪意を持って頻繁に大量のトラフィックを使用して、データベースにないレコードをクエリし、キャッシュを破壊し続けると、データベースは強制終了されます。キャッシュが問題です。
2つのオプションがあります。1つはキャッシュにnull値を追加することです。データベースのクエリが失敗した場合、値をnullに設定して、次回データベースにアクセスできないようにすることができます。これは簡単で便利ですが、それは少しスペースの無駄です。
2つ目の解決策は、ブルームフィルター(質問点)を使用し、キャッシュとWebサーバーの間にブルームフィルターのレイヤーを追加し、アクセスしたキーを記録することです。これにより、キャッシュの故障の問題も解決できます。
キャッシュアバランシェの問題
キャッシュアバランシェは、特定の時点で同時にキャッシュが無効化された場合に発生します。たとえば、キャッシュは無効化時間を設定するため、リンクで多くのキャッシュブレークダウンの問題が発生します。
分散ロックを追加することは解決策であり、ロックを取得する要求のみがデータベースにアクセスできます。ただし、これは一時的な解決策です。リクエストが多すぎると、多数のスレッドがブロックされ、メモリが損傷します。
データをウォームアップし、無効化時間を分散して設定します。これにより、キャッシュアバランシェの可能性を減らすことができます。
キャッシュの可用性を向上させるために、同じ単一のキャッシュポイントがキャッシュアバランシェの隠れた危険になります。ほとんどのキャッシュミドルウェアは、redisマスタースレーブ+センチネルアーキテクチャなどの高可用性アーキテクチャを提供します。