ScaNN インデックスを作成する

このページでは、保存されたエンベディングを使用してインデックスを生成し、AlloyDB for PostgreSQL で ScaNN インデックスを使用してエンベディングをクエリする方法について説明します。エンベディングの保存の詳細については、ベクトル エンベディングを保存するをご覧ください。

AlloyDB alloydb_scann は、Google が開発した PostgreSQL 拡張機能であり、ScaNN アルゴリズムによる非常に効率的な最近傍インデックスを実装しています。

ScaNN インデックスは、近似最近傍検索用のツリーベースの量子化インデックスです。HNSW と比較して、インデックスの構築時間が短く、メモリ使用量も小さくなります。また、ワークロードに応じて HNSW よりも QPS が速くなります。

始める前に

インデックスの作成を開始する前に、以下の前提条件を整える必要があります。

  • AlloyDB データベースのテーブルにエンベディング ベクトルが追加されている

  • Google が AlloyDB 用に拡張した pgvector に基づく vector 拡張機能と alloydb_scann 拡張機能がインストールされている。

    CREATE EXTENSION IF NOT EXISTS alloydb_scann CASCADE;
    
  • 自動的にチューニングされる ScaNN インデックスを作成する場合は、scann.enable_preview_features フラグが有効になっていることを確認してください。プレビュー機能を有効にしない場合や、本番環境のインスタンスの場合は、代わりに特定のパラメータを使用して ScaNN インデックスを作成できます。

自動的にチューニングされる ScaNN インデックスを作成する

自動インデックス機能を使用すると、インデックス作成を簡素化して、検索パフォーマンスに最適化されたインデックスや、インデックスのビルド時間と検索パフォーマンスのバランスが取れたインデックスを自動的に作成できます。

AUTO モードを使用する場合は、使用する距離関数とともに、テーブル名とエンベディング列を指定するだけで済みます。検索パフォーマンスに合わせてインデックスを最適化することや、インデックスのビルド時間と検索パフォーマンスのバランスを取ることが可能です。

MANUAL モードを使用して、他のインデックス チューニング パラメータをきめ細かく制御してインデックスを作成することもできます。

AUTO モードで ScaNN インデックスを作成する

AUTO モードでインデックスを作成する前に注意すべき点は次のとおりです。

  • AlloyDB は、データが不十分なテーブルの ScaNN インデックスを作成できません。
  • AUTO モードでインデックスを作成する場合、num_leaves などのインデックス作成パラメータを設定できません。
  • AUTO モードで作成されたすべてのインデックスに対して、デフォルトで自動メンテナンスが有効になります。

AUTO モードでインデックスを作成するには、フィーチャー トグル scann.enable_zero_knob_index_creation を有効にします。これにより、自動メンテナンスが有効になります。フラグを有効にしたら、次のコマンドを実行します。

  CREATE INDEX INDEX_NAME ON TABLE
  USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
  WITH (mode='AUTO');

次のように置き換えます。

  • INDEX_NAME: 作成するインデックスの名前(例: my-scann-index)。インデックス名はデータベース全体で共有されます。各インデックス名がデータベース内の各テーブルで一意であることを確認します。

  • TABLE: インデックスを追加するテーブル。

  • EMBEDDING_COLUMN: vector データを格納する列。

  • DISTANCE_FUNCTION: このインデックスで使用する距離関数。次のいずれかを選択します。

    • L2 距離: l2

    • ドット積: dot_product

    • コサイン距離: cosine

  • OPTIMIZATION(省略可): デフォルトでは、検索用に最適化されたインデックスが作成されます。次のいずれかに設定します。

    • SEARCH_OPTIMIZED(デフォルト): ベクトル検索の再現率とベクトル検索のレイテンシの両方を最適化します。ただし、インデックスのビルド時間が長くなります。
    • BALANCED: インデックスのビルド時間と検索パフォーマンスのバランスが取れたインデックスを作成します。

MANUAL モードで ScaNN インデックスを作成する

scann.enable_preview_features フラグを有効にして、チューニング パラメータをきめ細かく制御する場合は、MANUAL モードでインデックスを作成できます。

MANUAL モードで ScaNN インデックスを作成するには、次のコマンドを実行します。

  CREATE INDEX INDEX_NAME ON TABLE
  USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
  WITH (mode='MANUAL', num_leaves=NUM_LEAVES_VALUE, quantizer =QUANTIZER, max_num_levels=MAX_NUM_LEVELS);

次のように置き換えます。

  • INDEX_NAME: 作成するインデックスの名前(例: my-scann-index)。インデックス名はデータベース全体で共有されます。各インデックス名がデータベース内の各テーブルで一意であることを確認します。

  • TABLE: インデックスを追加するテーブル。

  • EMBEDDING_COLUMN: vector データを格納する列。

  • DISTANCE_FUNCTION: このインデックスで使用する距離関数。次のいずれかを選択します。

    • L2 距離: l2

    • ドット積: dot_product

    • コサイン距離: cosine

  • NUM_LEAVES_VALUE: このインデックスに適用するパーティションの数。1~1,000 万の任意の値に設定します。この値を選択する方法の詳細については、ScaNN インデックスをチューニングするをご覧ください。

  • QUANTIZER: 使用する量子化方式のタイプ。使用できるオプションは次のとおりです。

    • SQ8: クエリのパフォーマンスと再現率の損失のバランスを取ります。通常、再現率の損失は 1~2% 未満です。これはデフォルト値です。
    • AH: カラム型エンジンが有効になっており、インデックスとテーブルのデータが、構成されたサイズに応じてカラム型エンジンに入力されている場合は、クエリのパフォーマンスが向上する可能性があります。AHSQ8 と比較して、最大 4 倍圧縮されます。詳細については、ScaNN のチューニングに関するベスト プラクティスをご覧ください。
    • FLAT: 検索パフォーマンスを犠牲にして、99% 以上の再現率を実現します。
  • MAX_NUM_LEVELS: K 平均法クラスタリング ツリーのレベルの最大数。2 レベルのツリーベースの量子化の場合は 1(デフォルト)、3 レベルのツリーベースの量子化の場合は 2 に設定します。

他のインデックス作成またはクエリ ランタイム パラメータを追加して、インデックスをチューニングできます。詳細については、ScaNN インデックスをチューニングするをご覧ください。

特定のパラメータを使用して ScaNN インデックスを作成する

アプリケーションに再現率とインデックスのビルド時間に関する特定の要件がある場合は、インデックスを手動で作成できます。ワークロードに基づいて、2 レベルまたは 3 レベルのツリー インデックスを作成できます。チューニング パラメータの詳細については、ScaNN インデックスをチューニングするをご覧ください。

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

保存済みベクトル エンベディングを含む列に ScaNN アルゴリズムを使用した 2 レベルのツリー インデックスを適用するには、次の DDL クエリを実行します。

CREATE INDEX INDEX_NAME ON TABLE
USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (num_leaves=NUM_LEAVES_VALUE, quantizer =QUANTIZER);

次のように置き換えます。

  • INDEX_NAME: 作成するインデックスの名前(例: my-scann-index)。インデックス名はデータベース全体で共有されます。各インデックス名はデータベース内の各テーブルで一意となるようにしてください。

  • TABLE: インデックスを追加するテーブル。

  • EMBEDDING_COLUMN: vector データを格納する列。

  • DISTANCE_FUNCTION: このインデックスで使用する距離関数。次のいずれかを選択します。

    • L2 距離: l2

    • ドット積: dot_product

    • コサイン距離: cosine

  • NUM_LEAVES_VALUE: このインデックスに適用するパーティションの数。1~1,000 万の任意の値に設定します。この値を選択する方法の詳細については、ScaNN インデックスをチューニングするをご覧ください。

  • QUANTIZER: 使用する量子化方式のタイプ。使用できるオプションは次のとおりです。

    • SQ8: クエリのパフォーマンスと再現率の損失のバランスを取ります。通常、再現率の損失は 1~2% 未満です。これはデフォルト値です。
    • AH: カラム型エンジンが有効になっており、インデックスとテーブルのデータが、構成されたサイズに応じてカラム型エンジンに入力されている場合は、クエリのパフォーマンスが向上する可能性があります。AHSQ8 と比較して、最大 4 倍圧縮されます。詳細については、ScaNN のチューニングに関するベスト プラクティスをご覧ください。
    • FLAT: 検索パフォーマンスを犠牲にして、99% 以上の再現率を実現します。

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

保存されたベクトル エンベディングを含む列に ScaNN アルゴリズムを使用して 3 レベルのツリー インデックスを作成するには、次の DDL クエリを実行します。

CREATE INDEX INDEX_NAME ON TABLE
  USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
  WITH (num_leaves=NUM_LEAVES_VALUE, max_num_levels = 2);

インデックスを作成すると、指定されたテキストで類似クエリを作成する手順に沿って、インデックスを使用する最近傍探索クエリを実行できます。

インデックス パラメータは、QPS とリコールのバランスを適切にとるように設定する必要があります。ScaNN インデックスのチューニングの詳細については、ScaNN インデックスをチューニングするをご覧ください。

このインデックスを、vector ではなく real[] データ型を使用するエンベディング列に作成するには、列を vector データ型にキャストします。

CREATE INDEX INDEX_NAME ON TABLE
USING scann (CAST(EMBEDDING_COLUMN AS vector(DIMENSIONS)) DISTANCE_FUNCTION)
WITH (num_leaves=NUM_LEAVES_VALUE, max_num_levels = MAX_NUM_LEVELS);

DIMENSIONS は、エンベディング列のディメンション幅に置き換えます。ディメンションの確認方法については、ベクトル関数vector_dims 関数をご覧ください。

一貫した検索エクスペリエンスを実現するには、ScaNN インデックスを作成するときに自動メンテナンスを有効にします。詳細については、ベクトル インデックスを維持するをご覧ください。この機能はプレビュー版でご利用いただけます。

インデックス作成の進行状況を確認するには、pg_stat_progress_create_index ビューを使用します。

SELECT * FROM pg_stat_progress_create_index;

phase 列には、インデックス作成の現在のステータスが表示されます。インデックスの構築フェーズが完了すると、インデックスの行が表示されなくなります。

平均的な再現率と QPS のバランスを考慮してインデックスをチューニングするには、ScaNN インデックスをチューニングするをご覧ください。

空のテーブルまたは小さなテーブルでインデックスの作成を強制する

AlloyDB には、空のテーブル、または行数が非常に少ないテーブルに ScaNN インデックスが作成されないようにする検証機能があります。これは、パフォーマンスが最適にならない可能性があるためです。ただし、開発やテストのシナリオによっては、空のテーブルや小さなテーブルにインデックスを作成することが必要な場合があります。こうした場合は、インデックスの作成を強制できます。

インデックスの生成を強制するには、次の操作を行います。

  1. データベースで scann.allow_blocked_operations creation セッション レベルのパラメータを true に設定します。

    SET scann.allow_blocked_operations = true;
    
  2. データベースでこれらのクエリを実行するユーザーに SUPERUSER 権限を割り当てます。

    CREATE USER USER_NAME WITH SUPERUSER PASSWORD PASSWORD;
    

    次のように置き換えます。

    • USER_NAME: 権限を付与するユーザーの名前。
    • PASSWORD: ユーザーのパスワード

インデックスを並行して構築する

AlloyDB は、データセットと、インデックスをすばやく構築するために選択したインデックスのタイプに応じて、複数の並列ワーカーを自動的に生成する場合があります。

並列インデックス構築は、3 レベルの ScaNN インデックスを作成する場合や、データセットが 1 億行を超える場合にトリガーされます。

AlloyDB は並列ワーカーの数を自動的に最適化しますが、max_parallel_maintenance_workersmax_parallel_workersmin_parallel_table_scan_size PostgreSQL クエリ プランニング パラメータを使用して並列ワーカーをチューニングできます。

クエリを実行する

エンベディングをデータベースに保存してインデックスを作成した後は、データのクエリを開始できます。alloydb_scann 拡張機能を使用して一括検索クエリを実行することはできません。

エンベディング ベクトルの最も近いセマンティック ネイバーを見つけるには、次のサンプルクエリを実行します。ここでは、インデックスの作成時に使用した距離関数を設定します。

  SELECT * FROM TABLE
  ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY 'EMBEDDING'
  LIMIT ROW_COUNT

次のように置き換えます。

  • TABLE: テキストを比較するエンベディングを含むテーブル。

  • INDEX_NAME: 使用するインデックスの名前。例: my-scann-index

  • EMBEDDING_COLUMN: 保存されたエンベディングを含む列。

  • DISTANCE_FUNCTION_QUERY: このクエリで使用する距離関数。インデックスの作成時に使用した距離関数に基づいて、次のいずれかを選択します。

    • L2 距離: <->

    • 内積: <#>

    • コサイン距離: <=>

  • EMBEDDING: 保存されているセマンティック ネイバーの中で最も近いものを見つけるエンベディング ベクトル。

  • ROW_COUNT: 返される行数。

    最も適合するものが 1 つだけ必要な場合は、1 を指定します。

embedding() 関数を使用してテキストをベクトルに変換することもできます。embedding()real 配列を返すため、embedding() 呼び出しを vector に明示的にキャストしてから、いずれかの最近傍演算子((L2 距離の場合は <->)に適用する必要があります。こうした演算子では、ScaNN インデックスを使用して、意味的に最も類似したエンベディングを含むデータベース行を見つけます。

次のステップ