ファイルキャッシュの内部構造: キャッシュの分割、複数キュー管理、エビクション、ウォームアップ
基本原理
キャッシュの分割とプリフェッチの仕組み
Doris は対象ファイルを 1 MB 境界に揃えたスライスに分割します。各スライスのダウンロードが完了すると、ローカルファイルシステム上に独立したブロックファイルとして保存します。この細かな分割により、大きなファイル全体をキャッシュして領域を無駄にすることを避け、アクセスの集中するデータのヒット状況をより正確に追跡してエビクションできます。
ローカルファイルのディレクトリ構成
キャッシュデータは、複数のディスク上にある複数のディレクトリに分散できます。Doris は対象ファイルのパスのハッシュを計算し、そのハッシュをブロックファイルのパスの最下層ディレクトリ名に使用します。そのディレクトリ内では、各ブロックファイルに対象ファイル内の オフセット を名前として付け、データを複数のディレクトリに均等に分散します。
例: 対象ファイルのパスが /remote/data/datafile1 で、計算されたハッシュが 12345 の場合、対応するブロックファイルのパスは次のとおりです。
/cache/123/12345/<offset>
ここで <offset> は、元のファイル内におけるそのブロックのデータのオフセットです。
複数キューの仕組み
Doris はキャッシュ汚染を防ぎ、ヒット率を高めるために、キャッシュデータを種類に応じて異なるキューに振り分けます。以下に優先度の高い順に示します。
| キュー | 内容 | 優先度 | 備考 |
|---|---|---|---|
| TTL キュー | TTL 属性が設定されたデータ | 最高 | TTL が期限切れになるまではエビクションされません。TTL はテーブル単位の属性です。たとえば 3600 に設定すると、データ取り込み後、最大 1 時間キャッシュに保持されます。ローカルに保持する必要がある、小規模で頻繁にアクセスされるテーブルに適しています。 |
| Index キュー | インデックスデータ(転置インデックスを除く) | 高 | クエリのフィルタリングを高速化します。転置インデックスはサイズが大きいため、Normal キューに配置されます。 |
| Normal キュー | 通常のデータ(TTL 属性なし) | 中 | ほとんどのデータが格納されるキューです。 |
| Disposable キュー | 一時データ(Compaction 中に読み取られるデータなど) | 最低 | 使用後、最初にエビクションされます。 |
エビクションの仕組み
エビクションが発生する条件
| 発生条件 | 説明 |
|---|---|
| 領域不足による受動的なエビクション | ローカルディスクの空き領域や inode がなくなった場合、またはキャッシュ使用量が設定された容量の上限に達した場合に発生します。 |
| 高水位しきい値での能動的なエビクション | キャッシュ使用量が高水位しきい値に達すると、Doris は古いキャッシュエントリを事前に非同期で削除し、同期的なエビクションによって書き込みがブロックされるのを防ぎます。 |
| ガベージコレクションによるエビクション | Compaction や Schema Change の元データ、インポートのコミット失敗後にロールバックされたデータ、DROP TABLE または DROP PARTITION 操作の残存データを能動的に削除します。 |
| TTL 期限切れによる降格 | TTL エントリの期限が切れると、Normal キューに降格され、通常の LRU エビクションの対象になります。 |
エビクション対象の選択方法
領域の割り当て比率: 複数のキューがディスク領域を共有し、各キューには割り当て比率の上限が個別に設定されます(たとえば、Normal キューの上限は全領域の 40%)。ほかのキューが割り当て分をまだ使い切っていない場合、あるキューは残りの領域をすべて使用できます。種類ごとのデータ量が増えるにつれ、各キューの使用比率は設定値に徐々に近づきます。
エビクションの順序: 書き込み先のキューに十分な領域がない場合、Doris は割り当て比率を超えているデータを次の順序でエビクションします(各キュー内では、LRU によってエビクションするエントリの順序が決まります)。
Disposable → Normal → Index → TTL
ほかのキューからエビクションしても領域が不足する場合は、書き込み先のキュー自体から LRU に従ってエントリをエビクションします。
ホットデータのエビクションを避けるための推奨事項
- 十分なディスク領域を確保する: キャッシュの削除には多少の遅れがあるため、常に余裕を持たせてください。経験上、ファイルキャッシュ容量を頻繁にクエリされるホットデータ量の約 1.5 倍 に設定すると、高いヒット率を得られます。
- 大規模なクエリを分離する: 大規模なクエリを専用クラスターに振り分け、キャッシュを占有してほかのクエリのヒット率を下げることを防ぎます。
ウォームアップの仕組み
キャッシュのウォームアップでは、後続のクエリがローカルキャッシュに直接ヒットして性能を高められるよう、データを事前にキャッシュへ読み込みます。Doris には次の三つのウォームアップ方法があります。
手動ウォームアップ
指定したテーブルやパーティションを対象に、または別のクラスターのキャッシュ内容を参照して、ウォームアップを開始できます。データの取得元は常にリモートストレージであり、ほかの BE ノードではありません。クエリの実行経路上でほかの BE のキャッシュから読み取る機能は、ピアキャッシュ読み取りとして別途提供されます。ウォームアップの流れは次のとおりです。
- ウォームアップコマンドを実行します。システムは対象を tablet の集合に変換し、対応する BE に振り分けます。
- 各 BE は担当する tablet のすべてのデータファイルを順次読み取り、ローカルファイルキャッシュに書き込みます。
- タスクは、それぞれ最大 20 GB のバッチで実行されます。各バッチの完了後にチェックポイントが保存されるため、中断後に再開できます。
- BE が停止した場合、またはジョブを手動でキャンセルした場合、すべての BE がダウンロードを停止し、ウォームアップは終了します。
SHOW WARM UP JOB でタスクの状態(FINISHED / CANCELLED / RUNNING)と全体の進捗を確認できます。同じテーブルとパーティションを再びウォームアップすると、Doris は既存のデータを自動的に検出し、キャッシュ済みのデータを再ダウンロードせずに差分更新を行います。
データのリバランシングによって開始されるウォームアップ
負荷のリバランシング(スケーリングやノード障害に伴うもの)によって tablet が新しい BE に移行すると、新しい BE は古い BE に RPC を送信してキャッシュメタデータを取得し、そのメタデータに基づいてデータをローカルファイルキャッシュへ再ダウンロードします。古いノード上のキャッシュデータは、tablet 情報のクリーンアップ時に能動的にエビクションされ、領域が解放されます。
注: 移行完了からキャッシュのダウンロード準備が整うまでの間は、ファイルキャッシュにヒットしない場合があります。明示的な有効化が必要なピアキャッシュ読み取りを使用すると、この間、新しい BE はリモートストレージに戻らず、ソース BE のキャッシュから不足しているブロックを直接取得できます。
コンピュートクラスター間の自動ウォームアップ(バージョン 3.1 以降)
コンピュートとストレージを分離したデプロイメントでは、複数のコンピュートクラスター間でファイルキャッシュを自動的に同期できます。Doris には次の二つの同期方法があります。
| 方法 | 使用する場面 | 説明 |
|---|---|---|
| 定期ウォームアップ | データのリアルタイム性が重要でない場合 | WARM UP SQL ステートメントで同期間隔を指定します。ジョブは、指定したテーブルとパーティションの差分データを対象クラスターに定期的に同期します。 |
| データ取り込みまたは Compaction によって開始されるウォームアップ | リアルタイム性が必要な場合 | データ取り込みジョブがコミットフェーズに入ると、ソースクラスターの BE は対象クラスターの対応する BE に、新たにアップロードされたデータをダウンロードするよう通知します。Compaction の完了時も同様の経路で通知されます。FE は対象クラスターの tablet 配置情報を管理します。 |
シナリオ別の分析
クエリのシナリオ
クエリ中のファイルキャッシュの処理は次のとおりです。
- Scanner の読み取り要求: クエリが到着すると、Scanner コンポーネントは必要なデータファイルを読み取る準備をします。
- ローカルキャッシュの確認: Scanner はまずローカルファイルキャッシュを確認し、ファイルパスとオフセットに基づいてキャッシュメタデータを照合します。
- キャッシュヒット: 一致するキャッシュデータが見つかると、システムは BlockFile ハンドルの集合を返し、Scanner はリモートストレージにアクセスせずにローカルでデータを読み取ります。
- キャッシュミス: キャッシュにない範囲については、Scanner がリモートストレージからデータをダウンロードし、ファイルキャッシュに書き込んでから返します。領域はエビクションポリシーに従って解放されます。ピアキャッシュ読み取りが有効(明示的な有効化が必要)な場合、Scanner はまずほかの BE のファイルキャッシュを試し、どの BE にもデータがない場合にのみリモートストレージからダウンロードします。
データ取り込みのシナリオ
データ取り込み中のファイルキャッシュの処理は次のとおりです。
- リモートストレージへのアップロード: 取り込まれたデータは、まずリモートストレージに書き込まれます。
- ローカルキャッシュへの非同期書き込み: Doris は同時にデータをローカルファイルキャッシュへ非同期で書き込むため、取り込み直後のクエリが直接キャッシュにヒットできます。
- キャッシュキューの割り当て: データの種類と TTL 属性に基づいて、対応するキュー(TTL キュー、Index キュー、Normal キュー)にデータが書き込まれます。
Compaction のシナリオ
Doris には二種類の Compaction があります。
- Cumulative Compaction: 差分データのセグメントをマージします。
- Base Compaction: ベースラインのデータバージョン(バージョン 0 から始まるもの)と差分データのバージョンをマージします。
二種類の Compaction では、異なるキャッシュ書き込み戦略を使用します。
| Compaction の種類 | キャッシュ書き込み戦略 |
|---|---|
| Cumulative Compaction | 後続のクエリを高速化するため、データ取り込み時と同様に、出力データをリモートストレージへアップロードすると同時にファイルキャッシュへ書き込みます。 |
| Base Compaction | デフォルトでは、大量のコールドデータによるキャッシュ汚染を避けるため、キャッシュに十分な領域がある場合にのみデータを書き込みます。BE パラメータ enable_file_cache_keep_base_compaction_output = true を設定すると書き込みを強制できますが、ほかのホットデータがエビクションされる可能性があります。 |
ローカルキャッシュ容量が限られている場合は、インデックスのみを書き込むポリシーを有効にして、Compaction の出力に含まれる Segment データが大量のキャッシュ領域を能動的に占有しないようにできます。
enable_file_cache_write_index_file_only=true: Compaction を含め、コンピュートとストレージを分離したモードでのすべての rowset 書き込みに適用されます。Segment データは能動的にキャッシュされませんが、Segment のフッターと内部インデックスの範囲、および独立した転置インデックスファイルは引き続きキャッシュに書き込まれます。enable_file_cache_write_base_compaction_index_only=trueまたはenable_file_cache_write_cumu_compaction_index_only=true: 通常ならキャッシュに書き込まれる、該当する Compaction の出力だけを制限します。Segment ファイルはスキップされ、独立した転置インデックスファイルは保持されますが、Segment のフッターと内部インデックスの範囲は事前に読み込まれません。
パラメータの優先順位、設定例、制限事項については、ファイルキャッシュの設定を参照してください。
予定: Doris の将来のバージョンでは、過去のクエリ統計に基づく適応的な書き込み戦略が提供される予定です。
再起動後のキャッシュ読み込み
バージョン 3.1 より前は、再起動後に LRU キューの順序を復元できなかったため、ホットデータがエビクションされ、ヒット率が低下していました。
バージョン 3.1 では、LRU 情報の永続化が導入されました。 読み込みの流れは次のとおりです。
- 定期的なダンプ: 各 LRU キューの順序情報が定期的にディスクへ永続化されます。
- 再起動時の読み込み: ノードの再起動時にディスクからダンプデータを読み取り、キャッシュキューの状態を復元します。
- 完全性を確保するためのディスク全体のスキャン: ダンプのタイミングによって生じるメタデータとディスクの不整合を修正するため、再起動後にディスク全体をスキャンしてデータの整合性を確保します。
- クエリを契機とする並行した非同期読み込み: ディスク全体のスキャン中も、BE はクエリを処理できます。クエリがまだスキャンされていないデータを対象とする場合、システムはそのデータを先に読み込み、クエリの遅延を抑えます。
スケーリングのシナリオ
水平方向のスケールアウト
Doris はリバランシングによって tablet を新しい BE ノードへ移行します。移行先の BE は、移行元の BE のキャッシュメタデータに基づいてデータをローカルストレージへ再ダウンロードし、新しいノード上のクエリもファイルキャッシュにヒットできるようにします。ピアキャッシュ読み取りを使用すると、ダウンロードの完了前でも、移行先の BE での最初の読み取り時に移行元の BE のキャッシュからブロックを直接取得できます。
水平方向のスケールイン
Tablet のリバランシング後、クラスター全体のファイルキャッシュ容量が実際のキャッシュデータ量を下回る場合、エビクションの仕組みによって超過分のデータが自動的に削除されます。
垂直方向のスケールアウト
| スケールアウト方法 | 手順 |
|---|---|
| 単一ディスクの容量を増やす | 次のコマンドを実行して、領域の変更を BE に通知します: curl http://<BE_IP>:<WEB_PORT>/api/file_cache?op=reset&capacity=<new capacity in bytes> |
| ディスクを追加する | 推奨しません。 Doris は現在、再ハッシュを実装しておらず、ディスク間のリバランシングをサポートしていません。キャッシュディレクトリの数を変更すると、キャッシュの検索に失敗する可能性があります。ディスクの追加が必要な場合は、キャッシュを消去し、必要に応じて再度ウォームアップしてください。 |
垂直方向のスケールイン
ディスク領域を減らす場合も、上記の reset コマンドを実行する必要があります。キャッシュ容量が実際のキャッシュデータ量を下回ると、エビクションの仕組みが作動し、データが自動的に削除されます。
スケーリング後のウォームアップに関する注意点
水平方向のスケールアウトとスケールインには、tablet のリバランシングが伴います。ウォームアップの効果を確保するため、移行が安定してからウォームアップを実行してください。FE メトリクス doris_fe_tablet_num を監視すると、移行が完了したかどうかを判断できます。グラフが変動せず横ばいで安定していれば、移行は完了しており、ウォームアップを実行できます。
よくある質問
質問: キャッシュヒット率が低く、クエリ性能が改善しません。どのように調査すればよいですか?
- ファイルキャッシュ用ディスクに十分な空き領域があるか確認してください。推奨キャッシュ容量は、ホットデータ量の少なくとも 1.5 倍です。
- 大規模なクエリによってホットデータが頻繁にエビクションされていないか確認してください。大規模なクエリの分離を検討してください。
- 再起動後の短期間にヒット率が低いのは正常です(LRU の復元には時間がかかります)。事前に手動ウォームアップを実行できます。
質問: スケールアウト後、クエリがキャッシュにヒットしません。どうすればよいですか?
- Tablet のリバランシングが完了していることを確認してください(
doris_fe_tablet_numのグラフが横ばいで安定していること)。 - 対象のテーブルとパーティションに対して手動ウォームアップコマンドを実行してください。
- 水平方向のスケールアウト後、新しい BE でのデータのダウンロードには時間がかかります。この間にキャッシュミスが発生するのは想定内です。明示的に有効化したピアキャッシュ読み取りを使用すると、この期間の影響を短縮できます。有効になっているか確認してください。
質問: Base Compaction によってホットデータがエビクションされますか?
- デフォルトでは、Base Compaction は十分な領域がある場合にのみキャッシュへ書き込み、ホットデータを能動的にエビクションしません。
enable_file_cache_keep_base_compaction_output = trueが有効な場合、Base Compaction のデータはキャッシュに強制的に書き込まれ、ホットデータがエビクションされる可能性があります。実際のワークロードに基づいて、このトレードオフを評価してください。
質問: TTL の期限が切れると、データはすぐに削除されますか?
- いいえ、すぐには削除されません。TTL の期限が切れると、データは Normal タイプに降格され、Normal キューに入って通常の LRU エビクションの対象になります。最終的にいつ削除されるかは、エビクションの仕組みによって決まります。
質問: 手動ウォームアップでは、キャッシュ済みのデータの再ダウンロードを避けられますか?
- はい。ウォームアップを実行すると、Doris はキャッシュ済みのデータを自動的に識別し、新規または変更されたデータだけを差分としてダウンロードします。