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ログをキャッシュするために使用)、location cache(データコピーの場所をキャッシュするために使用)、schema cache(テーブルのSchema情報をキャッシュするために使用)、bloom filter cache(静的データのBloomfilterをキャッシュし、空検索を高速にフィルタリングするために使用)などがあります。OceanBaseデータベースは統一されたキャッシュフレームワークを設計しており、すべての異なるテナントの異なるタイプのキャッシュはこのフレームワークによって統一的に管理されます。異なるタイプのキャッシュには異なる優先順位が設定され、各キャッシュはそれぞれの優先順位とデータアクセスのヒートに応じて相互にスペースを競合します。異なるテナントについては、対応するテナントのメモリ使用量の上限と下限が設定され、各テナントのキャッシュはそれぞれのテナントのメモリ上下限およびサーバー全体のメモリ上限に応じて相互にスペースを競合します。

上図は、典型的なテーブル取得の例として、OceanBaseデータベースで現在クエリプロセスにサービスを提供しているさまざまなキャッシュを示しています。
BloomFilter Cache
OceanBaseデータベースの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:Partitionの位置情報をキャッシュし、クエリのルーティングを支援します。
Schema Cache:データテーブルのメタ情報をキャッシュし、実行計画の生成およびその後のクエリに使用されます。
Clog Cache:clogデータをキャッシュし、特定の状況下でのPaxosログの取得を高速化します。
......
これほど多様な種類のキャッシュをより汎用的にサポートするためには、可変長データを処理する問題が生じます。OceanBaseデータベースの基盤となるキャッシュフレームワークは、メモリを複数の2MBサイズのメモリブロックに分割し、メモリの申請と解放はすべて2MB単位で行われます。可変長データは、2MBサイズのメモリブロック内に単純にパックされます。データの迅速な位置付けをサポートするため、ハッシュマップには対応するデータへのポインタが格納されており、全体の構造は以下の図のようになります。

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