Lance クエリのベストプラクティス
このガイドを使用して、Lance クエリの BE ごとのキャッシュ予算とインデックスセグメントのレイアウトを選択します。まずワークロードを確認し、キャッシュに保持する必要があるデータ量を見積もり、想定する同時実行数で検証します。ここで示す例は容量計画の出発点であり、ベンチマーク済みの最適値や BE のメモリ上限ではありません。
Lance Catalog は実験的機能であり、Apache Doris 4.2 以降でサポートされています。まず Lance Catalog を使用してアクセスを設定し、Reader の互換性を確認してください。Doris は既存の Lance インデックスを読み取ります。インデックスの構築や保守には、互換性のある Lance ツールを使用してください。
スカラーフィルターをベクトル検索または全文検索と組み合わせる場合は、フィルターの配置、整合したインデックス構築、Profile の診断について Lance Hybrid Search Performance Best Practices を参照してください。
1. 調整方法を選択する
| ワークロード | 開始点 | 検証内容 |
|---|---|---|
| 単一クエリのレイテンシーが低いベクトル検索 | 頻繁に検索されるベクトルパーティションを保持し、セグメントガイドを使用して、利用可能な BE 数に対してバランスの取れたセグメント数を比較する | 同程度の再現率での P95 レイテンシー、最も遅いスキャナー、ピークメモリ |
| 高同時実行ベクトル検索 | 異なるクエリベクトルがアクセスするホットパーティションの和集合に予算を割り当て、各クエリの分割数を最大化するのではなく、同時実行検索のためにメモリと CPU を確保する | 目標同時実行数でのスループットと P95 レイテンシー、キャッシュの再読み込み、BE ごとのメモリ |
| BTree の範囲/ポイントフィルター、または Bitmap/LabelList フィルター | ルックアップ構造を含むホットページまたはビットマップエントリを見積もり、述語/インデックスが使用されていることを確認する | ページロード、選択率、一時的な行 ID セット、出力列の読み取り |
| 全文検索またはフレーズ検索 | 有効化されている場合は辞書、ドキュメントメタデータ、転置リスト、位置情報を見積もり、ウォームアップに多様な語句を含める | 一般的な語句のクエリ、フレーズクエリのメモリ、候補のマージ |
| ベクトル、スカラー、全文クエリが BE を共有する | 各クエリの共有ワーキングセットを加算して一緒にテストする | あるクエリ種別が別のクエリ種別のエントリを追い出していないか |
| 出力列の取得またはスキャンが支配的 | データファイルの読み取りと独立したディスクデータキャッシュを調べる | 有効バイト数と読み取り増幅、デコードまたは取得が支配的かどうか |
スカラーフィルターはベクトルクエリの Prefilter の一部になる場合もあるため、1 つのクエリがスカラーとベクトルの両方のインデックス内容を必要とすることがあります。シナリオを相互排他的とみなさず、該当する行をすべて選択してください。
2. 入力を収集してメモリ予算を設定する
各 BE について、または現実的な配置条件で代表的な BE を選び、次の入力を収集します。
- ワークロード: 目標 QPS/同時実行数、レイテンシー目標、必要な再現率、
top_k、フィルター、フレーズクエリ、出力列。 - リソース: クエリに使用できる BE、CPU 容量、他の Doris ワークロードとプロセス/OS の要件を差し引いた後に使用できるメモリ。
- インデックス: 種類、バージョン、物理セグメント数、カバレッジ、ベクトルの次元数/エンコーディング、IVF パーティション数、スカラー値の幅または文字列長。
- 再利用: 代表的なクエリ期間にアクセスされる異なるパーティション、ページ、語句、またはビットマップエントリ。1 つのクエリが返す行数ではなく、クエリ間のワーキングセットを使用します。
- クエリのピーク: 想定する同時実行数でのアクティブなパーティションロード、Prefilter/結果の行 ID セット、候補ヒープ、デコード、再ランキング、二段階目の取得に必要なメモリ。
index formulasを使用してペイロードを見積もり、合計予算が次を満たすことを確認します。
available_Lance_cache_budget_on_BE
= BE memory budget
- other BE memory needs
- peak concurrent query working memory
- safety headroom
index_cache_target + metadata_cache_target
<= available_Lance_cache_budget_on_BE
これは計画上の確認であり、強制的なメモリ予約ではありません。キャッシュ容量はクエリメモリやプロセス RSS を制限しません。再利用可能なキャッシュワーキングセットに同時実行クエリ数を掛けてはいけません。代わりに、クエリが追加で使用するワーキングメモリを予約し、同時実行クエリが異なるデータにアクセスする場合は、キャッシュ対象データの和集合が大きくなることを考慮します。
パラメーターの定義とデフォルト値は BE Configuration にあります。各 BE の be.conf に lance_* BE パラメーターを設定し、その BE を再起動します。これらは Catalog プロパティや SQL セッション変数ではありません。インデックスセグメント数と IVF 構築パラメーターは Lance のインデックス構築ツールで設定し、これらのキャッシュパラメーターでは設定しません。
3. インデックスのワーキングセットを見積もる
各 BE が再利用する必要のあるインデックスパーティションを対象に、インデックスキャッシュのサイズを設定します。ストレージ上のインデックス全体、キャッシュされたワーキングセット、クエリのピークメモリは異なる量です。以下の見積もりは、Doris がサポートする 6 種類のベクトルインデックスタイプを対象としています。これらは計画用の見積もりであり、正確なキャッシュ計測値やメモリ上限ではありません。
インデックスタイプごとのベクトルペイロード
次の記号を使用します。すべての式はバイト単位で、1 GiB = 1073741824 bytes です。
| 記号 | 意味 |
|---|---|
N | 見積もり対象パーティションに含まれるインデックスベクトル数。通常の単一ベクトル列では、インデックス化された行数です。マルチベクトル列では、インデックス化されたサブベクトルを数え、行マッピングと再ランキング用に別途予算を確保します。 |
D | ベクトルの次元数。 |
w | 保存された非量子化ベクトル要素あたりのバイト数。Float16 は 2、Float32 は 4、Float64 は 8。バイナリ uint8 ベクトルでは実際のバイト長を D とし、w=1 とします。 |
m、b | PQ サブベクトル数と、サブベクトルコードあたりのビット数。これらはインデックス構築設定であり、クエリパラメーターではありません。 |
G | ロードされた HNSW グラフが占有するメモリ。すべてのレベルと補助構造を含みます。 |
該当するコンポーネントをすべて加算し、ベクトルコードだけを計算しないでください。 最初の表ではベクトルごとのペイロードを分けています。続く表では共有構造と HNSW グラフのコンポーネントを示します。すべての式はロード済みバッファーを見積もります。Measure と記された行も予算に含める必要があり、ゼロコストではありません。共有アロケーションは 1 回だけ数えます。
| インデックスタイプ | ベクトル値 / コード | UInt64 行 ID | HNSW グラフ | その他の必要な予算 |
|---|---|---|---|---|
IVF_FLAT | N * D * w | 8 * N | なし | 下記の IVF 構造 + 共通オーバーヘッド |
IVF_SQ | SQ8 の場合 N * D | 8 * N | なし | IVF 構造 + SQ メタデータ + 共通オーバーヘッド |
IVF_PQ | N * ceil(m * b / 8) | 8 * N | なし | IVF 構造 + PQ コードブック + 共通オーバーヘッド |
IVF_HNSW_FLAT | N * D * w | 8 * N | G を追加 | IVF 構造 + 共通オーバーヘッド |
IVF_HNSW_SQ | SQ8 の場合 N * D | 8 * N | G を追加 | IVF 構造 + SQ メタデータ + 共通オーバーヘッド |
IVF_HNSW_PQ | N * ceil(m * b / 8) | 8 * N | G を追加 | IVF 構造 + PQ コードブック + 共通オーバーヘッド |
PQ で b=8 の場合、コードはベクトルごとに m バイトを占有します。b=4 の場合、サポートされる偶数の m では m/2 バイトを占有します。たとえば、100 万ベクトルで m=96、b=8 の場合、コードは 96000000 バイト、行 ID は 8000000 バイトで、合計は 104000000 バイト(約 99.2 MiB)です。強く圧縮されたインデックスの見積もりでも、行 ID を省略してはいけません。
| 追加コンポーネント | 適用対象 | ロード済みペイロード / 予算方法 |
|---|---|---|
| IVF セントロイド | 6 種類すべて | 保持するセントロイドセットごとに FP32 で num_partitions * D * 4。その他のレイアウトでは実際の要素幅を使用 |
| IVF パーティションディレクトリ / サブインデックスメタデータ | 6 種類すべて | Measure 保持されるパーティションのオフセット、長さ、Reader 構造。ベクトル値やセントロイドとは別 |
| PQ コードブック | IVF_PQ、IVF_HNSW_PQ | 保持する標準 FP32 コードブックごとに 2^b * D * 4 |
| SQ 量子化器メタデータ | IVF_SQ、IVF_HNSW_SQ | Measure 保持される量子化器の境界/パラメーター。バージョン間で固定レイアウトと仮定しない |
| ノルム、行カバレッジ構造、追加マッピング | Reader が保持する場合 | Measure 実際の配列/セット。マルチベクトル行マッピングには別予算が必要 |
| インデックスオブジェクト、Arrow validity バッファー、余剰容量、コピー | 6 種類すべて | Measure 論理ペイロード以外のアロケーション。共有バッファーは 1 回だけ数える |
独立したインデックスセグメントは、別々のセントロイドセットとコードブックを保持する場合があります。共有アロケーションをパーティションごとに重複して計算したり、すべてのコピーが共有されると仮定したりしないでください。追加構造は、組み込まれている Lance バージョンに依存します。
HNSW グラフコンポーネント
グラフはベクトルコードと行 ID に追加して計上します。 UInt32 のノード/隣接ノード ID と、保持される場合は Float32 のエッジ距離を持つ Arrow ベースの Lance グラフについて、ロードされたすべてのレベルにわたる保存済み有向隣接エントリ数を E、それらのレベルにおけるノード出現数を V、独立して保持されるグラフバッチ数を A とします。複数のレベルに存在するノードは、V に複数回加算されます。
| グラフコンポーネント | 主なペイロード(バイト) | カウント方法 |
|---|---|---|
| 隣接ノード ID | 4 * E | 有向隣接エントリごとに UInt32。すべてのレベルを数える |
| 保存されたエッジ距離 | 4 * E | 隣接エントリごとに Float32。Reader が距離列をスキップする場合はゼロ |
| グラフノード ID | 4 * V | ノード出現ごとに UInt32。データセットの UInt64 行 ID とは別 |
| 隣接リストオフセット | 4 * (V + A) | Int32 のリストオフセット。各バッチに終端オフセットを 1 つ含む |
| 距離リストオフセット | 4 * (V + A) | 2 番目の Int32 リストオフセット配列。Reader が距離列をスキップする場合はゼロ |
| 距離を含む小計 | 8 * E + 12 * V + 8 * A | 大きなバッチでは約 8 * E + 12 * V。完全な G ではない |
| 距離を含まない小計 | 4 * E + 8 * V + 4 * A | ノード ID と隣接リストだけを保持する場合に使用。2 つの小計を両方加算しない |
| レベルオフセット、エントリポイント、ノード検索構造 | Measure | ロードされた各グラフの保持メタデータと検索アロケーションを含める |
| Validity バッファー、グラフオブジェクト、余剰アロケーション容量 | Measure | 上記バッファー以外の実際のアロケーションを加算し、共有バッファーを二重計上しない |
完全なグラフ予算 G | 主な小計 + 実測したグラフ追加分 | ベクトル/行 ID ペイロードやその他のインデックス構造に加算する |
これはレイアウト固有の見積もりであり、ノードあたりの固定バイト数を保証するものではありません。HNSW の m はグラフの接続性を制御し、PQ の m とは無関係です。G の代わりに N * m * 4 を使わないでください。実際の次数、上位レベル、保持される距離、Reader のバージョンが影響します。代表的なパーティションをロードし、保持されるアロケーションを測定して調整してください。
たとえば、キャッシュされた IVF_HNSW_PQ パーティションに 100 万ベクトルがあり、PQ が m=96、b=8、E=32000000、V=1100000、A=1 だとします。この Reader が距離列を保持すると仮定します。これらのグラフ値は説明用の入力であり、HNSW の m から導出されたものではありません。
| コンポーネント | 例のペイロード |
|---|---|
| PQ コード | 96000000 バイト |
| データセット行 ID | 8000000 バイト |
| 主なグラフバッファー | 8 * 32000000 + 12 * 1100000 + 8 = 269200008 バイト、約 256.7 MiB |
| コード + 行 ID + 主なグラフバッファー | 373200008 バイト、約 355.9 MiB |
| 代替: 距離をスキップした場合の主なグラフバッファー | 4 * 32000000 + 8 * 1100000 + 4 = 136800004 バイト、約 130.5 MiB |
| 代替合計: コード + 行 ID + 距離なしのグラフバッファー | 240800004 バイト、約 229.6 MiB |
| いずれの場合も追加するもの | 上表のグラフ追加分、IVF セントロイド/ディレクトリ、PQ コードブック、共通オーバーヘッド |
Reader が保持する列に一致する例を選択してください。どちらの小計も、完全なキャッシュ容量の推奨値ではありません。
上流の Lance Performance Guide では、ベクトルペイロードとインデックス構築メモリの見積もりを説明しています。Lance vector index format では、IVF パーティション、サブインデックス、量子化ストレージの分離について説明しています。最新の内容には、Doris に組み込まれた Lance バージョンより新しい機能が記載されている場合があります。互換性については Lance Catalog のサポートマトリックスを使用してください。
スカラーおよび全文インデックスのペイロード
スカラーインデックスも見積もれますが、入力はインデックスファミリーごとに異なります。クエリが返す行だけでなく、1 つの BE に実際に保持されるエントリを数えます。以下の方法は Lance のストレージレイアウトを説明するものであり、Doris の述語プッシュダウンやインデックス互換性の保証を拡張するものではありません。組み込まれた Lance バージョンがそのインデックスをサポートしていることと、クエリが実際にそれを使用していることを確認してください。
BTree。 ロードされたリーフページには値と UInt64 行 ID が含まれます。キャッシュされたリーフページ内のレコード数を R、そのページ数を H、固定幅の値サイズを w、それらのページ内の平均文字列長をバイト単位で L とします。
| リーフページのコンポーネント / 表現 | 値バッファー | オフセット | UInt64 行 ID |
|---|---|---|---|
| Int32 / Float32 | 4 * R | なし | 8 * R |
| Int64 / Float64 / 64 ビットタイムスタンプ | 8 * R | なし | 8 * R |
| その他の固定幅値 | w * R; ビットパックされた Boolean には実際のビットマップバッファーを使用 | なし | 8 * R |
| Arrow Utf8 / Binary | R * L | Int32 オフセットの場合 4 * (R + H) | 8 * R |
| Arrow LargeUtf8 / LargeBinary | R * L | Int64 オフセットの場合 8 * (R + H) | 8 * R |
リーフページの小計は、3 つのペイロード列の合計です。 開かれた各セグメントについて、次のコンポーネントを追加します。ここで P はそのセグメント内のすべてのリーフページ数であり、キャッシュされたページだけではありません。
| 追加の BTree コンポーネント | 主なペイロード / 予算方法 |
|---|---|
| ルックアップの最小値 | 固定幅値の場合は P * w。可変幅値の場合は実際の値/オフセットバッファー |
| ルックアップの最大値 | 固定幅値の場合は P * w。可変幅値の場合は実際の値/オフセットバッファー |
| ルックアップ NULL カウント | UInt32 レイアウトの場合 4 * P |
| ルックアップページ番号 | UInt32 レイアウトの場合 4 * P |
| 固定幅ルックアップバッファーの小計 | P * (2 * w + 8)。下記の構造を除く |
| NULL ページリストとルックアップオブジェクト | Measure 保持されるリスト、オブジェクト、実装固有のコピー |
| リーフ/ルックアップ validity バッファー、Arrow オブジェクト、余剰容量 | Measure 論理値/オフセット/行 ID 以外のアロケーション |
文字列の式は、表に示した Arrow レイアウトを前提としています。辞書または view 表現では、実際のバッファーを使用してください。ディスク上の圧縮サイズは、デコード後のページサイズを決定しません。BTree のデフォルトページサイズは 4096 レコードであり、バイトではありません。インデックスの実際のページサイズを使用してください。ルックアップはインデックスワーキングセットに属するため、ページを記述するという理由だけで BE メタデータキャッシュの予算に入れてはいけません。
たとえば、1 億個の Int64 値には、リーフ値/行 ID ペイロードとして 100000000 * 16 = 1600000000 バイト(約 1.49 GiB)があります。キャッシュされたページが 1000 万レコードをカバーする場合、そのペイロードは約 152.6 MiB です。1 ページあたり 4096 レコードとすると、インデックス全体には約 24415 ページがあります。ルックアップバッチの min/max/count/page-number ペイロードは、追加構造を除いて約 572 KiB です。10 件の一致行が返されても、10 レコードだけがキャッシュされるわけではありません。Reader はページをロードします。
Bitmap、LabelList、NGram。 下表の該当するコンポーネントを合計します。メンバーシップビットマップのコストを、一致する行ごとに 8 バイトと自動的に見積もってはいけません。LabelList は異なるラベルごとにメンバーシップを数えるため、1 行が複数のラベルビットマップに現れることがあります。NGram は異なる gram/行メンバーシップを数えます。1 行内で同じ gram が繰り返し出現しても、メンバーシップは増えません。この NGram インデックスは、n-gram トークナイザーを使用する FTS インデックスとは異なります。
| インデックスファミリー | コンポーネント | ロード済みペイロード / 予算方法 |
|---|---|---|
| Bitmap | キールックアップ | Measure 保持されるキー値、エントリ参照、ルックアップオブジェクト |
| LabelList | ラベルルックアップ / ラッパー | Measure 異なるラベルキー、エントリ参照、ラッパーオブジェクト |
| NGram | Gram ルックアップ | Measure 保持される gram、エントリ参照、ルックアップオブジェクト |
| 3 種類すべて | メンバーシップビットマップ: 配列コンテナ | 65536 値の範囲で c 個の UInt16 エントリを持つ通常の Roaring 配列コンテナでは約 2 * c |
| 3 種類すべて | メンバーシップビットマップ: 密コンテナ | 65536 値の範囲をカバーする通常の密コンテナでは 8192 バイト |
| 3 種類すべて | メンバーシップビットマップ: run / 完全フラグメント表現 | Measure 実際の表現。各コンテナで選択された表現だけを使用し、すべての代替表現を合計しない |
| 3 種類すべて | ビットマップディレクトリ / 容量 | Measure フラグメント/上位ビットディレクトリ、コンテナオブジェクト、余剰容量 |
| 存在する場合 | NULL / null-list 追跡 | Measure 保持される NULL メンバーシップ構造を別途計上 |
| 各ファミリーの合計 | ルックアップ + ロード済みメンバーシップ表現 + ディレクトリ + NULL 追跡 | 共有アロケーションは 1 回だけ数える。シリアライズされたビットマップサイズは常駐メモリを保証しない |
たとえば、256 個のロード済み密コンテナには 2 MiB のビットセットペイロードがあります。これはキー、ディレクトリ、NULL 追跡、オブジェクトオーバーヘッドを含みません。
その他のスカラーおよび全文インデックス。 各行は別個のコンポーネントを示します。小計行は前のコンポーネントをまとめたものであり、再度加算してはいけません。BE に保持される内容だけを数えます。Measure は Reader の実際のアロケーションが必要という意味であり、そのコンポーネントを省略できるという意味ではありません。
| インデックスファミリー | コンポーネント | ロード済みペイロード / 予算方法 |
|---|---|---|
| FTS / Inverted | 用語辞書 | Measure ロードされる用語、辞書構造、ルックアップアロケーション |
| FTS / Inverted | ドキュメントメタデータ | Measure 保持されるドキュメントマッピング、長さ、その他の Reader メタデータ |
| FTS / Inverted | プレーン転置行 ID | T 個の UInt64 用語/ドキュメントエントリでは 8 * T |
| FTS / Inverted | プレーン転置頻度 | Float32 頻度では 4 * T。ID + 頻度の合計は 12 * T |
| FTS / Inverted | 位置情報(有効な場合) | 保存された O 個の Int32 出現位置では 4 * O |
| FTS / Inverted | 位置オフセット | 実際に保持されるオフセット配列のバイト数。4 * O とは別 |
| FTS / Inverted | 圧縮転置レイアウト | 対応するプレーンレイアウトの式の代わりに、実際に保持される圧縮ブロックを使用 |
| FTS / Inverted | スコアリング/ブロックメタデータとアロケーションオーバーヘッド | Measure 保持されるブロック記述子、スコアリングデータ、オブジェクト、余剰容量 |
| ZoneMap | 最小値 | ロードされた Z 個のゾーンと幅 w の固定幅値では、論理バイト数 Z * w |
| ZoneMap | 最大値 | 論理バイト数 Z * w。min + max の合計は 2 * Z * w |
| ZoneMap | カウントとゾーン境界 | Measure 保持されるカウントと境界。部分ゾーンを含め、Fragment ごとにゾーンを数える |
| ZoneMap | NULL 追跡とボックス化値/オブジェクトオーバーヘッド | Measure オプションの NULL 行セットと実際のスカラーアロケーション。論理的な min/max バイト数を上回る場合がある |
| BloomFilter | フィルタービット配列 | 実際に割り当てられたフィルターバイトを合計。F バイトの同じサイズのフィルターが Z 個なら Z * F |
| BloomFilter | ゾーン記述子 / フィルターオブジェクト | Measure 保持される記述子、フィルターパラメーター、オブジェクトオーバーヘッド |
| BloomFilter | NULL 追跡 | Measure オプションの NULL 行セット |
| RTree | バウンディングボックス座標 | 2D エントリごとに 4 つの Float64 座標で 32 * Q。Q はキャッシュされたすべてのツリーレベルのエントリ数 |
| RTree | 行 / 子ページ ID | UInt64 ID では 8 * Q。座標 + ID の合計は 40 * Q |
| RTree | ツリーメタデータとページオフセット | Measure 保持されるツリー記述子とページオフセットバッファー |
| RTree | NULL 追跡、Arrow/オブジェクト、余剰容量 | Measure 実際の追加アロケーション |
| JSON-path ラッパー | 基盤となるインデックス | 選択したスカラーインデックスファミリーの完全なコンポーネント予算を使用 |
| JSON-path ラッパー | パスとラッパー構造 | Measure 基盤となるインデックスに加えて保持されるパスとラッパーのアロケーション |
Bloom フィルターでは、ビルダーが実際にブロック単位で丸めて割り当てたサイズを使用する必要があります。行数と偽陽性率だけでは、すべてのレイアウトを説明できません。未知または実験的なレイアウトでは、別のファミリーの式を適用せず、ロード済みアロケーションを測定してください。一致する行が少なくても、辞書、ツリー、ドキュメントメタデータに相当な容量が必要になることがあります。
クエリのワーキングメモリは、すべてのキャッシュ表とは分けてください。 一時的な積集合/和集合、結果行 ID セット、近似フィルターの再確認、正確な RTree チェックのための元のジオメトリ読み取り、出力列の読み取りには、それぞれ独自の予算が必要です。
基盤構造については、上流の BTree format、Bitmap format、FTS format を参照してください。上記の式はロード済みペイロードを見積もるものであり、圧縮ファイルサイズやクエリ全体の RSS ではありません。
インデックスサイズから BE ごとのワーキングセットへ
セグメント s について、そのインデックス化されたベクトル数を N_s、IVF パーティション数を P_s、特定の BE に保持する異なるパーティション数を H_s とします。パーティションが十分に均衡している場合は、次のようになります。
cached_vectors_on_BE ≈ sum over segments (N_s * H_s / P_s)
index_cache_target ≈ cached vector/row-ID payload
+ cached HNSW graphs
+ centroids, codebooks and other index allocations
+ measured headroom
パーティションに偏りがある場合は、実際のパーティション行数を使用します。H_s はクエリ間のワーキングセットを表し、1 つのクエリの nprobes だけを表すものではありません。多様なクエリは最終的にすべてのパーティションに触れる可能性があります。繰り返しクエリや同時実行クエリは同じキャッシュ済みパーティションを共有できます。(dataset, index segment, partition) の異なる組み合わせを数え、その BE がアクセスするすべてのテーブル、インデックス、バージョンを含めます。1 つの BE のキャッシュで別の BE がウォームアップされることはなく、スケジューリングによってすべてのインデックスセグメントが常に同じ BE に配置される保証もありません。観測された配置と再利用状況が正当化しない限り、インデックス全体のサイズを BE 数で割らないでください。
クエリ時のメモリは別途必要です。インフライトのパーティションロード、キャッシュから追い出された後もクエリが保持するオブジェクト、Prefilter の状態、距離テーブル、候補ヒープ、デコード済みバッチ、再ランキング、二段階目の取得などです。top_k、ef、refine_factor、スキャン同時実行数、nprobes はこのメモリやアクセスされるワーキングセットに影響しますが、ベクトルあたりの保存バイト数は変えません。設定したキャッシュ容量は Lance や BE の RSS に対するハード上限ではありません。キャッシュを増やしても、距離計算やクエリごとのフィルタリング処理はなくなりません。
構成例と調整
1 つのインデックスセグメントに、FP32、768 次元の IVF_PQ インデックスがあり、1 億ベクトル、m=96、b=8、均衡した 4096 パーティションがあるとします。1 つの BE の代表的なワークロードが 1024 個の異なるパーティションに繰り返しアクセスするとします。
Full code/row-ID payload = 100000000 * (96 + 8) = 10400000000 bytes ≈ 9.69 GiB
Cached payload = 10400000000 * 1024 / 4096 = 2600000000 bytes ≈ 2.42 GiB
IVF centroids = 4096 * 768 * 4 = 12582912 bytes = 12 MiB
PQ codebook = 256 * 768 * 4 = 786432 bytes = 0.75 MiB
これが BE 上で唯一の大規模なインデックスワーキングセットで、メモリ予算が許す場合、この例では 4 GiB を初期インデックスキャッシュ割り当てとして設定するのが妥当です。計算されたペイロードに加えて、追加のインデックスアロケーション分を残せます。これは検証すべき初期値であり、普遍的なオーバーヘッド比率ではありません。ワークロードがインデックス全体を常駐させる必要がある場合、4 GiB では不足します。デフォルトの 10 GiB でさえ、他のアロケーションを含める前の 9.69 GiB のペイロードに近い値です。
4 GiB のインデックスキャッシュと 1 GiB のメタデータキャッシュを、ピーククエリメモリ、他の Doris ワークロード、プロセス/OS の余裕とともに収められる BE では、次を設定します。
lance_index_cache_size_bytes = 4294967296
lance_metadata_cache_size_bytes = 1073741824
これらの設定を有効にするには、その BE を再起動します。予算は各 BE に個別に適用します。これらは BE 構成パラメーターであり、Catalog プロパティや SQL セッション変数ではありません。
- 異なるクエリベクトル、フィルター、テーブルを含む代表的なクエリと同時実行数でウォームアップします。1 つのベクトルだけを繰り返すと、多様なワークロードのワーキングセットを過小評価します。
doris_be_lance_session_index_cache_usage_bytesとdoris_be_lance_session_index_cache_capacity_bytesを比較し、期間内のhits_total/misses_totalの増加量を観測します。完了した各クエリについて、LanceIndex内のLanceIndexCacheHits、LanceIndexCacheMisses、LanceIndexObjectLoads、LanceIndexComponentsLoadedも確認します。クエリキャッシュのルックアップとコンポーネントロードはスコープが異なり、BE 全体のメトリクス差分と同じものではありません。使用量が容量に近い状態でミスが続く場合、キャッシュの競合が示唆されることがあります。ただし、新しいパーティション、インデックスバージョン、異なる BE、保持されない内容を扱うストリーミングパスもミスを引き起こします。再利用と利用可能なメモリが正当化する場合にだけ容量を増やしてください。- BE のメモリ予算が許す場合は、メタデータキャッシュのサイズをデフォルトの 1 GiB から開始します。その
usage_bytesとヒット/ミスメトリクスを使用して調整します。ファイル/Fragment 数、スキーマ/ページメタデータ、削除情報、行 ID マッピングによって必要量が決まります。ベクトルインデックスサイズの一定割合ではありません。BE のメタデータキャッシュは FE のテーブルアクセスキャッシュとは別です。 - どちらかのキャッシュを増やす前に、想定する同時実行数で BE プロセスメモリとクエリピークを確認します。キャッシュヒット率が高くてもレイテンシーが高い場合、スコアリング、Prefilter、デコード、取得のコストが原因かもしれません。キャッシュを追加しても必ずしも有効とは限りません。
- 再ランキングや出力列の取得など、データファイルを繰り返し読み取る処理については、
lance_data_cache_disk_capacity_bytesを独立して設定します。これを増やしてもインデックスキャッシュは拡大せず、インデックスキャッシュミスを補うこともできません。インデックスファイルはこのディスクデータキャッシュをバイパスします。lance_data_cache_read_block_size_bytesは最初はデフォルトの 1 MiB のままにし、有効な読み取りと読み取り増幅を測定した後で変更します。
4. 共有キャッシュの予算を設定する
各 BE で Lance は共有セッションと 1 つのインデックスキャッシュ容量を使用します。ベクトルパーティション、BTree ページ、Bitmap/LabelList エントリ、全文インデックスの内容は、この領域を競合します。同じ BE でアクセスされる異なるテーブルや Catalog のインデックスも含まれます。キャッシュキーは ID を分離しますが、容量を予約するものではありません。Lance の BE キャッシュ設定には、テーブル単位またはインデックスタイプ単位のクォータや固定保持を制御する機能はありません。
キャッシュ容量を消費するのはロード済みの内容だけです。複数のインデックスを作成しただけで、それらがすべてロードされるわけではありません。クエリが使用を開始すると、1 つのインデックスをロードすることで別のインデックスのエントリが追い出されることがあります。たとえば、広い範囲の BTree スキャンや多様な FTS クエリによって、ウォーム状態だったベクトルパーティションが置き換えられる場合があります。その後のベクトルクエリでは、再度ロードが必要になります。インデックスバージョンの変更により、古いエントリが常駐している間に新しいエントリが追加されることもあります。実行中のクエリが参照しているオブジェクトは、追い出されてもすぐには解放されません。
共有アロケーションを二重計上せず、混合ワーキングセットを計画します。
index_cache_target_on_BE ≈ vector working set
+ BTree working set
+ Bitmap / LabelList / NGram working sets
+ FTS and other index working sets
+ measured headroom
たとえば、測定とペイロードの見積もりにより、ベクトルが 2.5 GiB、BTree が 0.25 GiB、Bitmap が 0.5 GiB、FTS が 0.75 GiB で、それぞれのインデックス構造を含むとします。合計ワーキングセットは 4 GiB です。5 GiB の初期インデックスキャッシュは、増加分と見積もり誤差として 1 GiB を残しますが、すべてのワークロードが収まることを保証するものではありません。BE に 1 GiB のメタデータキャッシュ、およびピーククエリメモリ、他の Doris ワークロード、プロセス/OS の余裕を収める余地がある場合は、次を設定します。
lance_index_cache_size_bytes = 5368709120
lance_metadata_cache_size_bytes = 1073741824
インデックスキャッシュとメタデータキャッシュの容量は別々です。メタデータエントリがインデックスエントリを直接追い出すことはなく、一方を増やしても他方は拡大しません。ただし、どちらも同じ BE プロセスのメモリを消費します。ディスクデータキャッシュは 3 つ目の予算であり、インデックスファイルはキャッシュしません。同じ BE 上で Catalog を分けてもインデックスキャッシュは分離されません。分離が必要なワークロードでは、デプロイメントがサポートする別個の BE リソースとルーティングを使用します。
競合を診断するには、まず 1 つのクエリファミリーをウォームアップしてレイテンシーとキャッシュメトリクスの差分を記録します。次に 2 つ目のファミリーを実行し、最初のファミリーを再実行します。インデックスバージョンと BE の配置が同等で、キャッシュ使用量が容量に近い状態でミスや再ロードが再び発生する場合は、追い出し圧力が示唆されます。セッションのヒット/ミスメトリクスはワークロードを集約するため、クエリレベルの LanceIndexCacheHits/LanceIndexCacheMisses、LanceIndexComponentsLoaded、LanceIOReadBytes と組み合わせて確認します。コンポーネントロード数はミス数ではなく、実行時の読み取りバイト数は実測したリモートトラフィックではありません。すべてのインデックスを事前ウォームアップすると、保持したかったワーキングセット自体が追い出される可能性があります。代表的な混合ワークロードでウォームアップし、再利用と利用可能なメモリが正当化する場合にだけ容量を増やしてください。
5. インデックスセグメントの粒度を選択する
インデックスセグメント、データ Fragment、IVF パーティション、BTree リーフページは異なる単位です。現在の Doris ベクトル検索パスでは、選択された各物理インデックスセグメントが 1 つのスキャン Split になり、カバレッジのない Fragment は Flat Search Split を追加します。FTS も物理インデックスセグメントを使用します。1 つのベクトルセグメント内の IVF パーティションが、複数の BE に分散される個別の Doris Split になることはありません。ただし、Lance は内部で処理を並列実行できます。通常のスカラー走査でセグメントに基づくグループ化が行われるかどうかは、選択したインデックスとプランによって異なります。すべてのスカラーインデックスがベクトル検索と同じ Split ポリシーに従うと仮定しないでください。
B をこのクエリで実際に使用できる BE 数、S を選択されたベクトルセグメント数とします。S が B より少ない場合、そのクエリには、すべての BE を使用するのに十分なインデックススキャン Split がありません。S を増やすとスケジューリングの機会は増えますが、すべての Split が同時に実行される保証はありません。スキャナーの制限、Lance の内部同時実行数、CPU、I/O、キャッシュ常駐、同時実行クエリも影響します。Doris のパイプラインインスタンスだけを増やしても、1 つのインデックスセグメントを分割することはできません。
大規模なデータセットでは、適度に均衡した B、2 * B、4 * B 個のベクトルセグメントを、公式の最適値や小規模データセットに対する要件ではなく、初期実験として比較します。8 個の利用可能な BE と 1 億ベクトルの例を示します。
| セグメント数 | セグメントあたりの平均ベクトル数 | 評価内容 |
|---|---|---|
| 8 | 12.5 million | BE をカバーするのに十分な Split で、セグメントあたりの処理が少ない |
| 16 | 6.25 million | スケジューリングの柔軟性が高く、1 つの大きなセグメントへの依存が小さい |
| 32 | 3.125 million | タスク数が増えるが、検索、ロード、マージのオーバーヘッドが増える可能性がある |
Fragment 数だけでなく、推定検索処理量とロード済みインデックスバイト数を均衡させます。同じ次元数とエンコーディングのベクトルでは、行数を均衡させることが出発点になります。HNSW グラフサイズ、IVF の偏り、スカラー選択率、ドキュメント長によってバランスが変わる場合があります。セグメントあたりの行数やファイルサイズに普遍的なしきい値はありません。高スループットのワークロードでは、同時実行クエリの容量を確保するために、クエリあたりのタスクを少なくする方が適する場合があります。単一クエリのレイテンシーは、Split を増やすことで改善する場合があります。
セグメントが増えると、セグメントごとのセットアップ、メタデータ/量子化器オブジェクト、ローカル候補生成も増えます。各ベクトル/FTS Split は最大 top_k + offset 個の候補を保持し、Doris はこの上限を S で割りません。したがって、スキャン側の候補上限は S * (top_k + offset) であり、実際の一致数に制限されます。top_k=1000、offset なし、32 Split の場合、Doris の削減前に最大 32000 個の候補があります。ローカル TopN によりネットワーク転送量は減る可能性があるため、これはネットワーク行数の保証ではありません。top_k が大きい場合は、候補処理が特に重要になります。
セグメント数は、各セグメントの IVF num_partitions およびクエリの nprobes と併せて調整します。均衡したパーティションでは、探索されるベクトル数の概算は sum(N_s * nprobes_s / P_s) であり、各セグメントでは N_s が上限になります。これは HNSW の比較回数の式ではありません。セグメントを再構築したりパーティション数を変更したりすると、同じ nprobes でも再現率が変わる可能性があります。再現率が同程度になる条件でレイテンシーを比較します。HNSW の ef と再ランキング設定も比較に影響します。
Lance ツールでセグメントを構築・保守し、目的の論理インデックスとしてコミットします。別名で作成した複数の全テーブルインデックスは、複数の物理セグメントを持つ 1 つのインデックスの代わりにはなりません。完全な Fragment のグループでバランスを取り、小規模な追加による小さなセグメントの蓄積を避け、組み込まれた Lance バージョンがサポートする保守 API を使用します。構築後、Doris の EXPLAIN/Profile で物理セグメント数とカバレッジのない Fragment 数を確認してください。SHOW INDEX の行数から推測しないでください。すべてのセグメントを 1 つにマージすると、Doris のスキャン並列性が低下する可能性があります。セグメントの構築とコミットの概念については Lance distributed indexing を参照してください。
BTree のポイントルックアップや選択性の高い Bitmap クエリでは、多数のセグメントに分割するとルックアップとオープン処理が増える一方、セグメントあたりの削減作業が少ないことがあります。FTS では、セグメントごとの辞書/転置検索と候補マージが追加されます。ベクトルのセグメント数をすべてのインデックスファミリーに適用せず、これらのワークロードを個別に評価してください。分割しても、行ごとに不要な Prefilter を構築する処理はなくなりません。Reader のフィルタリング動作を個別に確認してください。
6. 混合ワークロードを検証する
- インデックス選択、物理 Split、インデックスカバレッジを確認します。フォールバック走査と二段階目の取得をレイテンシー分析に含めます。
- 各インデックスの保持ペイロードを見積もり、共有ワーキングセットを加算します。並列ロード、行 ID セット、候補ヒープ、デコードを含むクエリメモリは別途確保します。
- 現実的な同時実行数で、コールドキャッシュとウォーム済みの混合ワークロードを比較します。P95 レイテンシー、スループット、該当する場合は再現率、BE ごとのピークメモリ、キャッシュ使用量とヒット/ミスの差分、インデックスロード I/O、スキャン時間の偏りを追跡します。
- 一度に 1 つの軸だけを変更します。キャッシュ容量、セグメント数、検索パラメーターのいずれかです。セグメントの再構築でモデルが変わった場合は、再現率を再確認します。スループット、レイテンシー、メモリが改善しなくなったら、Split やキャッシュを増やすのを停止します。
- 大幅な追加、インデックス再構築、BE のスケーリング、クエリ構成の変更後に再評価します。1 つのベクトルやテーブル向けに調整した構成は、すべてのワークロードに対する安定した予算ではありません。
7. リソースを増やす前に診断する
| 観測結果 | 最初に確認すること | 評価する対策 |
|---|---|---|
| キャッシュ使用量が容量に近い状態でインデックスミスが繰り返される | インデックス/バージョンの変更、BE 配置、合計ワーキングセット | メモリが許せば lance_index_cache_size_bytes を増やす、不要なウォームアップを減らす、または別の BE リソースにワークロードを分離する |
| スカラーまたは FTS クエリの後にベクトルクエリが遅くなる | 共有キャッシュの予算でクロスワークロードの追い出しを再確認する | 両方のクエリファミリーを合わせて予算化する。同じ BE 上の別 Catalog はキャッシュを分離しない |
| 大規模なインデックスデータセットをスキャンする BE が少ない | 物理セグメント Split 数、利用可能な BE、スキャナーのスケジューリング | より均衡したセグメントを比較する。パイプラインインスタンスを増やすだけではセグメントを分割できない |
| セグメントを増やすと CPU、メモリ、マージ時間が増える | Split あたりの候補数、top_k + offset、クエリ同時実行数 | セグメント数を減らす、またはアプリケーションが必要とする以上の候補を要求しない。検索設定を変更した場合は再現率を再確認する |
| キャッシュヒット率は高いがレイテンシーが高い | スコアリング、Prefilter 処理、デコード、二段階目の取得時間 | 支配的な段階を最適化する。インデックスキャッシュを増やしても計算や取得はなくならない |
| メタデータの再ロードが続く | メタデータキャッシュの使用量とヒット/ミス差分、Fragment/ファイル数、バージョン変更 | 再利用とメモリが許せば lance_metadata_cache_size_bytes を独立して調整する |
| キャッシュ使用量が容量未満なのにプロセスメモリが高い | 同時実行クエリのアロケーション、アクティブなロード、保持されるオブジェクト、他の BE ワークロード | 測定したピークに基づいて同時実行数またはキャッシュ予算を下げる。キャッシュ容量はプロセスメモリの上限ではない |
| データファイル I/O が高いまま | ディスクキャッシュのカウンター、再利用、読み取りブロックサイズ、クエリ出力列 | データキャッシュの予算または読み取り粒度を調整する。インデックスファイルはこのディスクデータキャッシュを使用しない |
Query Profile referenceを使用して、FE のメタデータ/計画、BE のインデックス/スキャン処理、二段階目の行 ID 取得を分離します。キャッシュカウンターと BE 全体のメトリクスについては、Checking Cache Effectivenessを使用してください。集計されたヒット率だけでは、どのインデックスが追い出されたか、どの段階がレイテンシーを支配しているかは特定できません。設定は一度に 1 つだけ変更し、各比較についてワークロード、インデックスバージョン、BE 配置、レイテンシー、スループット、再現率、ピークメモリを記録してください。