ベクトルクエリのパフォーマンスを調整する

このドキュメントでは、クエリのパフォーマンスを向上させ、レコードの検索精度を高めるためにインデックスをチューニングする方法について説明します。

ScaNN インデックスをチューニングする

ScaNN インデックスは、木量子化ベースのインデックスを使用します。ツリー量子化手法では、インデックスは量子化(ハッシュ化)関数とともに検索ツリーを学習します。クエリを実行すると、検索空間のプルーニングに検索ツリーが使用され、インデックスサイズの圧縮に量子化が使用されます。このプルーニングにより、クエリベクトルとデータベース ベクトルの類似度(距離)のスコアリングが高速化されます。

秒間クエリ数(QPS)と最近傍クエリでの高いレコード検索率の両方を実現するには、データとクエリに最も適した方法で ScaNN インデックスのツリーをパーティショニングする必要があります。

ScaNN インデックスを作成する前に、次の操作を行います。

  • データを含むテーブルがすでに作成されていることを確認します。
  • インデックスの生成中に問題が発生しないように、maintenance_work_mem フラグと shared_buffers フラグに設定する値がマシンのメモリの合計より小さいことを確認してください。

調整パラメータ

次のインデックス パラメータとデータベース フラグを組み合わせて使用することで、再現率と QPS の適切なバランスを見つけることができます。すべてのパラメータは、両方の ScaNN インデックス タイプに適用されます。

チューニング パラメータ 説明 パラメータの型
num_leaves このインデックスに適用するパーティションの数。インデックスの作成時に適用するパーティションの数は、インデックスのパフォーマンスに影響します。設定された数のベクトルのパーティションを増やすと、よりきめ細かいインデックスが作成され、再現率とクエリのパフォーマンスが向上します。ただし、インデックスの作成時間が長くなります。

3 レベルのツリーは 2 レベルのツリーよりも構築が速いため、3 レベルのツリー インデックスを作成するときに num_leaves_value を増やすと、パフォーマンスを向上させることができます。
  • 2 レベル インデックス: この値は、11048576 の範囲内の任意の値に設定します。

    正確な値を選択できない場合は、sqrt(ROWS) を開始点として使用します。ここで、ROWS はベクトル行の数です。各パーティションが保持するベクトルの数は、
    ROWS/sqrt(ROWS) = sqrt(ROWS) で計算されます。

    2 レベルのツリー インデックスは、ベクトル行数が 1,000 万未満のデータセットに作成できるため、各パーティションは sqrt(10M) 未満のベクトル(3200 ベクトル)を保持します。パフォーマンスを最適化するには、各パーティション内のベクトルの数を最小限に抑えることをおすすめします。
  • 3 レベル インデックス: この値は、11048576 の範囲内の任意の値に設定します。

    正確な値を選択できない場合は、power(ROWS, 2/3) を開始点として使用します。ここで、ROWS はベクトル行の数です。各パーティションが保持するベクトル数は
    ROWS/power(ROWS, 2/3) = power(ROWS, 1/3) で計算されます。

    3 レベルのツリー インデックスは、ベクトル行数が 1 億を超えるデータセットに作成できるため、各パーティションは
    power(100M, 1/3))を超えるベクトル(465 ベクトル)を保持します。パフォーマンスを最適化するには、各パーティション内のベクトルの数を最小限に抑えることをおすすめします。
インデックスの作成
quantizer K 平均法ツリーに使用する量子化ツールのタイプ。デフォルト値は SQ8 で、クエリのパフォーマンスを向上させます。

再現率を高めるには、FLAT に設定します。
インデックスの作成
enable_pca 主成分分析(PCA)を有効にします。これは、可能な場合はエンベディングのサイズを自動的に縮小するために使用される次元削減手法です。このオプションはデフォルトで有効になっています。

再現率の低下が観察された場合は、false に設定します。
インデックスの作成
scann.num_leaves_to_search データベース フラグは、再現率と QPS のトレードオフを制御します。デフォルト値は、num_leaves で設定された値の 1% です。

設定値が高いほど再現率は高くなりますが、QPS は低くなります。その逆も同様です。
クエリ ランタイム
scann.max_top_neighbors_buffer_size database フラグは、スキャンされた候補の近傍をディスクではなくメモリ内でスコアリングまたはランク付けすることで、フィルタされたクエリのパフォーマンスを向上させるために使用されるキャッシュのサイズを指定します。デフォルト値は 20000 です。

設定値が大きいほど、フィルタリングされたクエリの QPS は向上しますが、メモリ使用量は増加します。その逆も同様です。
クエリ ランタイム
scann.pre_reordering_num_neighbors データベース フラグを設定すると、最初の検索で候補のセットが特定された後の並べ替えステージで検討する近傍候補の数を指定できます。クエリで返す近傍の数より大きい値に設定します。

値セットが大きいほど再現率は高くなりますが、このアプローチでは QPS が低下します。
クエリ ランタイム
max_num_levels K 平均法クラスタリング ツリーの最大レベル数。
  • 2 レベルのツリー インデックス: デフォルトでは、2 レベルのツリーベースの量子化に設定されます。
  • 3 レベルのツリー インデックス: 3 レベルのツリーベースの量子化の場合は、明示的に 2 に設定します。
インデックスの作成

ScaNN インデックスをチューニングする

チューニング パラメータの設定方法を示す、2 レベルと 3 レベルの ScaNN インデックスの例を次に示します。

2 レベル インデックス

SET LOCAL scann.num_leaves_to_search = 1;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 1/2)]);

3 レベル インデックス

SET LOCAL scann.num_leaves_to_search = 10;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 2/3)], max_num_levels = 2);

ScaNN インデックスがすでに生成されているテーブルに対する挿入オペレーションまたは更新オペレーションは、学習済みツリーがインデックスを最適化する方法に影響します。テーブルが頻繁に更新または挿入される場合は、既存の ScaNN インデックスを定期的に再作成して、再現率を高めることをおすすめします。

インデックス指標をモニタリングして、インデックスの作成後に作成されたミューテーションの量を特定し、それに応じてインデックスを再作成できます。指標の詳細については、ベクトル インデックスの指標をご覧ください。

チューニングのベスト プラクティス

使用する ScaNN インデックスのタイプに応じて、インデックスのチューニングに関する推奨事項は異なります。このセクションでは、レジュールと QPS のバランスを最適にするためにインデックス パラメータを調整する方法について説明します。

2 レベル ツリー インデックス

データセットに最適な num_leavesnum_leaves_to_search の値を見つけるために推奨事項を適用する手順は次のとおりです。

  1. num_leaves をインデックス対象テーブルの行数の平方根に設定して、ScaNN インデックスを作成します。
  2. 目標の再現率範囲(95% など)に達するまで、テストクエリを実行し、scann.num_of_leaves_to_search の値を増やします。クエリの分析の詳細については、クエリを分析するをご覧ください。
  3. 以降のステップで使用する scann.num_leaves_to_searchnum_leaves の比率をメモします。この比率は、目標の再現率を達成するために役立つデータセットの近似値を提供します。

    高次元ベクトル(500 次元以上)を扱っていて、再現率を改善したい場合は、scann.pre_reordering_num_neighbors の値を調整してみてください。最初は、値を 100 * sqrt(K) に設定します。ここで、K はクエリで設定した上限です。
  4. クエリが目標レジュール達成後に QPS が低すぎる場合は、次の手順を行います。
    1. インデックスを再作成し、次のガイダンスに従って num_leavesscann.num_leaves_to_search の値を増やします。
      • num_leaves を行数の平方根の大きい係数に設定します。たとえば、インデックスで num_leaves が行数の平方根に設定されている場合は、平方根の 2 倍に設定してみてください。値がすでに 2 倍になっている場合は、平方根の 3 倍に設定してみてください。
      • 必要に応じて scann.num_leaves_to_search を増やし、ステップ 3 でメモした num_leaves との比率を維持します。
      • num_leaves は、行数を 100 で除した値以下の値に設定します。
    2. テストクエリをもう一度実行します。テストクエリを実行しながら、scann.num_leaves_to_search を減らして、再現率を高く保ちながら QPS を増やす値を見つけます。インデックスを再ビルドせずに、scann.num_leaves_to_search の値を変更してみます。
  5. QPS と再現率の範囲の両方が許容値に達するまで、手順 4 を繰り返します。

3 レベルのツリー インデックス

2 レベルのツリー ScaNN インデックスの推奨事項に加えて、次のガイダンスと手順を使用してインデックスをチューニングします。

  • max_num_levels を 2 レベル ツリーの 1 から 3 レベル ツリーの 2 に増やすと、インデックスの作成時間が大幅に短縮されますが、再現率の精度は低下します。次の推奨事項を使用して max_num_levels を設定します。
    • ベクトルの行数が 1 億行を超える場合は、値を 2 に設定します。
    • ベクトル行数が 1,000 万行未満の場合は、値を 1 に設定します。
    • ベクトル行数が 1, 000 万行から 1 億行の範囲にある場合は、インデックス作成時間と必要な再現率のバランスに基づいて、1 または 2 に設定します。

推奨事項を適用して、num_leaves インデックス パラメータと max_num_levels インデックス パラメータの最適な値を確認する手順は次のとおりです。

  1. データセットに基づいて、次の num_leavesmax_num_levels の組み合わせで ScaNN インデックスを作成します。

    • ベクトル行が 1 億行を超える場合: max_num_levels2 に、num_leavespower(rows, ⅔) に設定します。
    • ベクトル行が 1 億行未満の場合: max_num_levels1 に、num_leavessqrt(rows) に設定します。
    • 1,000 万行から 1 億行までのベクトル行: まず、max_num_levels1 に、num_leavessqrt(rows) に設定します。
  2. テストクエリを実行します。クエリの分析の詳細については、クエリを分析するをご覧ください。

    インデックスの作成時間が十分な場合は、max_num_levels 値を保持し、num_leaves 値をテストして、最適な再現率を実現します。

  3. インデックスの作成時間が長すぎる場合は、次の操作を行います。

    • max_num_levels の値が 1 の場合、インデックスを削除します。max_num_levels 値を 2 に設定してインデックスを再ビルドします。

      クエリを実行し、再現率を最適化するように num_leaves 値を調整します。

    • max_num_levels 値が 2 の場合、インデックスを削除します。同じ max_num_levels 値でインデックスを再ビルドし、num_leaves 値をチューニングして、再現率を最適化します。

IVF インデックスをチューニングする

listsivf.probesquantizer パラメータに設定した値を調整すると、アプリのパフォーマンスを最適化できます。

チューニング パラメータ 説明 パラメータの型
lists インデックスの構築中に作成されたリストの数。この値の設定の開始点は、最大 100 万行の場合は (rows)/1000、100 万行を超える場合は sqrt(rows) です。 インデックスの作成
quantizer K 平均法ツリーに使用する量子化ツールのタイプ。デフォルト値は SQ8 で、クエリのパフォーマンスが向上します。FLAT に設定すると、想起率が向上します。 インデックスの作成
ivf.probes 検索時に探索する最近傍リストの数。この値の出発点は
sqrt(lists) です。
クエリ ランタイム

チューニング パラメータが設定された IVF インデックスを示す次の例について考えてみましょう。

SET LOCAL ivf.probes = 10;

CREATE INDEX my-ivf-index ON my-table
  USING ivf (vector_column cosine)
  WITH (lists = 100, quantizer = 'SQ8');

IVFFlat インデックスをチューニングする

lists パラメータと ivfflat.probes パラメータに設定した値を調整すると、アプリケーションのパフォーマンスを最適化できます。

チューニング パラメータ 説明 パラメータの型
lists インデックスの構築中に作成されたリストの数。この値の設定の開始点は、最大 100 万行の場合は (rows)/1000、100 万行を超える場合は sqrt(rows) です。 インデックスの作成
ivfflat.probes 検索時に探索する最近傍リストの数。この値の出発点は
sqrt(lists) です。
クエリ ランタイム

IVFFlat インデックスを作成する前に、データベースの max_parallel_maintenance_workers フラグが、大規模なテーブルのインデックス作成を迅速化できる値に設定されていることを確認します。

チューニング パラメータが設定された IVFFlat インデックスを示す次の例について考えてみましょう。

SET LOCAL ivfflat.probes = 10;

CREATE INDEX my-ivfflat-index ON my-table
  USING ivfflat (vector_column cosine)
  WITH (lists = 100);

HNSW インデックスをチューニングする

mef_constructionhnsw.ef_search パラメータに設定した値を調整すると、アプリケーションのパフォーマンスを最適化できます。

チューニング パラメータ 説明 パラメータの型
m グラフ内のノードからの最大接続数。デフォルト値の 16(デフォルト)から始めて、データセットのサイズに応じて値を大きくしてテストできます。 インデックスの作成
ef_construction グラフの構築中に維持される動的候補リストのサイズ。このリストでは、ノードの最近傍の現在の最良の候補が常に更新されます。この値は、m 値の 2 倍を超える値に設定します(例: 64(デフォルト))。 インデックスの作成
ef_search 検索時に使用される動的候補リストのサイズ。この値を m または ef_construction に設定し、リコールを確認しながら変更できます。デフォルト値は 40 です。 クエリ ランタイム

チューニング パラメータが設定された hnsw インデックスを示す次の例について考えてみましょう。

SET LOCAL hnsw.ef_search = 40;

CREATE INDEX my-hnsw-index ON my-table
  USING hnsw (vector_column cosine)
  WITH (m = 16, ef_construction = 200);

クエリを分析する

次の SQL クエリの例に示すように、EXPLAIN ANALYZE コマンドを使用してクエリ分析情報を分析します。

  EXPLAIN ANALYZE SELECT result-column FROM my-table
    ORDER BY EMBEDDING_COLUMN ::vector
    USING INDEX my-scann-index
    <-> embedding('textembedding-gecko@003', 'What is a database?')
    LIMIT 1;

レスポンスの例 QUERY PLAN には、所要時間、スキャンまたは返された行数、使用されたリソースなどの情報が含まれます。

Limit  (cost=0.42..15.27 rows=1 width=32) (actual time=0.106..0.132 rows=1 loops=1)
  ->  Index Scan using my-scann-index on my-table  (cost=0.42..858027.93 rows=100000 width=32) (actual time=0.105..0.129 rows=1 loops=1)
        Order By: (embedding_column <-> embedding('textgecko@003', 'What is a database?')::vector(768))
        Limit value: 1
Planning Time: 0.354 ms
Execution Time: 0.141 ms

ベクトル インデックスの指標を表示する

ベクトル インデックスの指標を使用すると、ベクトル インデックスのパフォーマンスを確認して改善すべき領域を特定し、必要に応じて指標に基づいてインデックスをチューニングできます。

すべてのベクトル インデックス指標を表示するには、pg_stat_ann_indexes ビューを使用する次の SQL クエリを実行します。

SELECT * FROM pg_stat_ann_indexes;

指標の一覧の詳細については、ベクトル インデックスの指標をご覧ください。

次のステップ