OceanBaseデータベースのシェアードナッシング(Shared-Nothing、SN)モードのクラスタアーキテクチャでは、そのキャッシュ設計はOracleやMySQLとは大きく異なります。OceanBaseデータベースのストレージエンジンはLSM-Treeアーキテクチャに基づいており、すべての変更はMemTableにのみ書き込まれ、SSTableは読み取り専用であるため、OceanBaseデータベースのキャッシュは読み取り専用のキャッシュであり、ダーティページをフラッシュする関連ロジックがありません。これは従来のデータベースに比べて簡単です。しかし同時に、SSTableに格納されるデータに対してデータエンコーディングと圧縮が行われるため、保存するデータは固定長ではなく可変長であり、従来のデータベースと比較してキャッシュのメモリ管理ははるかに複雑になります。さらに、OceanBaseデータベースはマルチテナント向けの分散データベースシステムでもあり、従来のデータベースと同様にキャッシュのメモリエリミネーションを処理するだけでなく、キャッシュ内部でマルチテナントのメモリ隔離も必要です。
OracleやMySQLと同様に、OceanBaseデータベース内部にもさまざまな種類のキャッシュが存在します。SSTableデータをキャッシュするBlock Cache(OracleやMySQLのbuffer cacheに似ています)に加えて、row cache(データ行をキャッシュするため)、log cache(redo logをキャッシュするため)、location cache(データレプリカの位置をキャッシュするため)、schema cache(テーブルのSchema情報をキャッシュするため)、bloom filter cache(静的データのBloomfilterをキャッシュし、空クエリを高速にフィルタリングするため)などがあります。OceanBaseデータベースは統一されたキャッシュフレームワークを設計しており、すべての異なるテナントのさまざまな種類のキャッシュはこのフレームワークによって一元的に管理されます。異なる種類のキャッシュにはそれぞれ異なる優先順位が設定され、各キャッシュはそれぞれの優先順位とデータアクセスの頻度に基づいて相互に競合します。また、異なるテナントには対応するテナントのメモリ使用量の上限と下限が設定され、各テナントのキャッシュはそれぞれのテナントのメモリ上下限およびサーバー全体のメモリ上限に基づいて相互に競合します。

上図は、典型的なテーブル取得の例として、OceanBaseデータベースにおける現在のクエリプロセスをサポートするさまざまなキャッシュを示しています。
BloomFilter Cache
OceanBaseデータベースのBloomFilterはマクロブロック上に構築され、ユーザーの実際の空クエリ率に基づいて必要に応じて自動的に構築されます。マクロブロック上の空クエリ回数が特定のしきい値を超えると、自動的にBloomFilterが構築され、そのBloomFilterがキャッシュに格納されます。
Row Cache
各SSTableに対して具体的なデータ行をキャッシュし、Get/MultiGetクエリを実行する際に、対応するデータ行をRow Cacheに格納することで、次回その行をクエリする際に複数回の二分探索による行の検索を回避できます。
Block Index Cache
マイクロブロックのインデックスをキャッシュします。各SSTableはマクロブロックで構成されていますが、2MBの粒度はユーザーのクエリにとってはしばしば粒度が粗すぎるため、ユーザーのクエリ範囲に基づいてマクロブロック内で実際に必要なマイクロブロックを特定する必要があります。マイクロブロックインデックスは、各マクロブロック内のすべてのマイクロブロックの範囲を記述しており、特定のマクロブロックのマイクロブロックにアクセスする必要がある場合は、そのマクロブロックのマイクロブロックインデックスを事前にロードする必要があります。プレフィックス圧縮が行われているため、通常はサイズが小さく、OceanBaseデータベース内部では高い優先順位が付与されているため、一般的にヒット率は高いです。
Block Cache
OracleのBuffer Cacheに似ており、具体的なデータマイクロブロックをキャッシュします。各マイクロブロックは解凍された後にBlock Cacheにロードされるため、各キャッシュのサイズは可変長です。
Fuse Row Cache
LSM-Treeアーキテクチャでは、同一行の変更が異なるSSTableに存在する可能性があります。OceanBaseデータベースはストレージ占有をさらに最適化するため、ユーザーの更新ごとに増分データのみを保存します。そのため、クエリ時には各SSTableのクエリ結果を融合する必要があります。ユーザーが新たな更新をトリガーしなくなった場合、この融合結果はクエリに対して常に有効であるため、OceanBaseデータベースは融合結果をキャッシュするFuse Row Cacheも提供しており、一部のユーザーのホット行クエリをより大幅にサポートします。
OceanBaseデータベースは、上記のユーザークエリ関連のマルチレベルキャッシュに加えて、さまざまな他の種類のキャッシュもサポートしています。例えば:
Partition Location Cache:パーティションの位置情報をキャッシュし、クエリのルーティングを支援します。
Schema Cache:データテーブルのメタ情報をキャッシュし、実行計画の生成およびその後のクエリに使用されます。
Clog Cache:clogデータをキャッシュし、特定の状況下でのPaxosログのプルを高速化します。
......
これほど多くの種類のキャッシュをより汎用的にサポートするためには、可変長データの問題を処理する必要があります。OceanBaseデータベースの下層キャッシュフレームワークはメモリを複数の2MBサイズのメモリブロックに分割し、メモリの申請と解放はすべて2MB単位で行われます。可変長データは2MBサイズのメモリブロック内に単純にパックされます。データの迅速な位置特定をサポートするために、ハッシュマップ内に対応するデータへのポインタが格納されており、全体の構造は以下の図のとおりです。

キャッシュメモリは2MB単位で全体がエリミネーションされ、OceanBaseデータベースは各2MBメモリブロック上の各要素のアクセス頻度に基づいてスコアを計算します。アクセス頻度が高いメモリブロックほどスコアが高くなります。同時に、バックグラウンドスレッドが定期的にすべての2Mメモリブロックのスコアをソートし、スコアが低いメモリブロックをエリミネーションします。全体的なスコアはそれほど高くないものの内部にホットデータが存在する2MBメモリブロックについては、OceanBaseデータベースはそのホットデータを「冷たいブロック」から「熱いブロック」に移動させ、ホットデータがエリミネーションされるのを防ぎます。データ構造上、OracleやMySQLのようなLRUリンクリストは維持されていないため、データ読み取りにおいて、OceanBaseデータベースのキャッシュアクセスはほぼロックフリーです(HashMap上のBucketロックを除く)。これにより、ホットデータの高い同時アクセスにより適しています。エリミネーション時には、テナントのメモリ上限および下限を考慮し、各テナント内のキャッシュメモリ使用量を制御します。