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

ドキュメントのバージョンを選択してください。

保存されたエンベディングを使用して、AlloyDB Omni で ScaNN ベクトル インデックスを生成し、エンベディングをクエリします。

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

始める前に

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

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

自動的にチューニングされる ScaNN インデックスを使用すると、AlloyDB Omni がインデックス構造を管理してチューニングするため、インデックスの作成が簡素化されます。インデックス チューニングをきめ細かく制御する必要がある場合は、手動でチューニングされた ScaNN インデックスを作成します。

自動調整されたインデックスは、次の 2 つの方法で最適化できます。

  • (デフォルト)インデックスのビルド時間を犠牲にしてベクトル検索の再現率とレイテンシを向上させる
  • インデックスのビルド時間と検索パフォーマンスのバランスを取る

自動調整された ScaNN インデックスを作成するには、次のコマンドを実行します。

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)

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

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

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

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

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

    • L2 距離: l2

    • ドット積: dot_product

    • コサイン距離: cosine

このコマンドは、検索パフォーマンスに最適化された ScaNN インデックスを作成します。この設定を変更する場合は、次のコマンドを実行します。

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (MODE='AUTO',
      OPTIMIZATION='OPTIMIZATION')

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

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

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

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

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

    • L2 距離: l2

    • ドット積: dot_product

    • コサイン距離: cosine

  • (省略可)OPTIMIZATION: 次のいずれかに設定します。

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

    • BALANCED: インデックスのビルド時間と検索パフォーマンスのバランスを取ります。

    OPTIMIZATION を設定する場合は、MODE='AUTO' も含める必要があります。

手動で調整されたインデックスを作成する

アプリケーションに再現率とインデックスのビルド時間に関する特定の要件がある場合は、ScaNN インデックスを手動で作成してチューニングできます。

保存されたベクトル エンベディングを含む列に ScaNN インデックスを手動で作成するには、次のコマンドをご覧ください。

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

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      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 ~ 3,000 万の任意の値に設定します。この値を選択する方法の詳細については、ScaNN インデックスをチューニングするをご覧ください。
  • QUANTIZER: 使用する量子化方式のタイプ。ScaNN インデックスをカラム型エンジンに読み込んで、ベクトル検索をさらに高速化できます。次のいずれかを選択します。
    • (デフォルト)SQ8: クエリのパフォーマンスと再現率の損失のバランスを取ります。通常、これは 1 ~ 2% 未満です。
    • プレビューAH: 非対称ハッシュ(AH)は、SQ8 と比較して最大 4 倍圧縮されます。カラム型エンジンが有効になっており、インデックスとテーブルのデータがカラム型エンジンに入力されている場合は、クエリのパフォーマンスが向上する可能性があります。詳細については、ScaNN のチューニングに関するベスト プラクティスをご覧ください。
    • FLAT: 検索パフォーマンスを犠牲にして、99% 以上の再現率を実現します。

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

CREATE INDEX INDEX_NAME ON TABLE
       USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
WITH (mode='MANUAL',
      num_leaves=NUM_LEAVES_VALUE,
      quantizer=QUANTIZER,
      auto_maintenance=AUTO_MAINTENANCE,
      max_num_levels = 2);
  • INDEX_NAME: 作成するインデックスの名前。例: my_scann_index。インデックス名はデータベース全体で共有されます。各インデックス名がデータベース内の各テーブルで一意であることを確認します。
  • TABLE: インデックスを追加するテーブル。
  • EMBEDDING_COLUMN: `vector` データを格納する列。
  • DISTANCE_FUNCTION: このインデックスで使用する距離関数。次のいずれかを選択します。
    • L2 距離: l2
    • ドット積: dot_product
    • コサイン距離: cosine
  • NUM_LEAVES_VALUE: このインデックスに適用するパーティションの数。1 ~ 3,000 万の任意の値に設定します。この値を選択する方法の詳細については、ScaNN インデックスをチューニングするをご覧ください。
  • QUANTIZER: 使用する量子化方式のタイプ。ScaNN インデックスをカラム型エンジンに読み込んで、ベクトル検索をさらに高速化できます。次のいずれかを選択します。
    • (デフォルト)SQ8: クエリのパフォーマンスと再現率の損失のバランスを取ります。通常、これは 1 ~ 2% 未満です。
    • プレビューAH: 非対称ハッシュ(AH)は、SQ8 と比較して最大 4 倍圧縮されます。カラム型エンジンが有効になっており、インデックスとテーブルのデータがカラム型エンジンに入力されている場合は、クエリのパフォーマンスが向上する可能性があります。詳細については、ScaNN のチューニングに関するベスト プラクティスをご覧ください。
    • FLAT: 検索パフォーマンスを犠牲にして、99% 以上の再現率を実現します。
  • (省略可) AUTO_MAINTENANCE: インデックスの自動メンテナンスを有効にするか無効にするかを制御します。自動メンテナンスの詳細については、ベクトル インデックスを維持するをご覧ください。
    • (デフォルト)ON: AlloyDB Omni はインデックスの自動メンテナンスを実行します。
    • OFF: AlloyDB Omni は、インデックスに対して自動メンテナンスを実行しません。
  • max_num_levels = 2: K 平均法クラスタリング ツリーのセントロイド レベルの最大数。このパラメータを 2 に設定して、3 レベルのインデックスを作成します。

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

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 = 3);
  • INDEX_NAME: 作成するインデックスの名前。例: my_scann_index。インデックス名はデータベース全体で共有されます。各インデックス名がデータベース内の各テーブルで一意であることを確認します。
  • TABLE: インデックスを追加するテーブル。
  • EMBEDDING_COLUMN: `vector` データを格納する列。
  • DISTANCE_FUNCTION: このインデックスで使用する距離関数。次のいずれかを選択します。
    • L2 距離: l2
    • ドット積: dot_product
    • コサイン距離: cosine
  • NUM_LEAVES_VALUE: このインデックスに適用するパーティションの数。1 ~ 3,000 万の任意の値に設定します。この値を選択する方法の詳細については、ScaNN インデックスをチューニングするをご覧ください。
  • QUANTIZER: 使用する量子化方式のタイプ。ScaNN インデックスをカラム型エンジンに読み込んで、ベクトル検索をさらに高速化できます。次のいずれかを選択します。
    • (デフォルト)SQ8: クエリのパフォーマンスと再現率の損失のバランスを取ります。通常、これは 1 ~ 2% 未満です。
    • プレビュー AH: 非対称ハッシュ(AH)は、SQ8 と比較して最大 4 倍圧縮されます。カラム型エンジンが有効になっており、インデックスとテーブルのデータがカラム型エンジンに入力されている場合は、クエリのパフォーマンスが向上する可能性があります。詳細については、ScaNN のチューニングに関するベスト プラクティスをご覧ください。
    • FLAT: 検索パフォーマンスを犠牲にして、99% 以上の再現率を実現します。
  • max_num_levels = 3: K 平均法クラスタリング ツリーのセントロイド レベルの最大数。このパラメータを 3 に設定して、4 レベルのインデックスを作成します。

手動でチューニングされたインデックスを自動でチューニングされたインデックスに変換する

手動で調整されたインデックスを自動で調整されたインデックスに変換する手順は次のとおりです。

  1. 手動で調整したインデックスに定義されているすべてのクエリ パラメータをリセットします。

    ALTER INDEX INDEX_NAME RESET (PARAMETER_NAME);
    

    次の変数を置き換えます。

    • INDEX_NAME: 変換するインデックスの名前。例: my_scann_indexインデックス名はデータベース全体で共有されます。各インデックス名がデータベース内の各テーブルで一意であることを確認します。

    • PARAMETER_NAME: リセットするクエリ パラメータの名前を含むカンマ区切りのリスト。例: num_leaves, quantization

      num_leaves をリセットする前に、他のすべてのクエリ パラメータをリセットする必要があります。

  2. 手動で調整したインデックスを再作成して、自動調整されたインデックスに変換します。

    REINDEX INDEX CONCURRENTLY INDEX_NAME;
    

real[] データ型の ScaNN インデックスを作成する

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

CREATE INDEX INDEX_NAME ON TABLE
USING scann (CAST(EMBEDDING_COLUMN AS vector(DIMENSIONS)) DISTANCE_FUNCTION)

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

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

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

  • DIMENSIONS: モデルがサポートするディメンションの数。

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

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

    • L2 距離: l2

    • ドット積: dot_product

    • コサイン距離: cosine

インデックス登録の進行状況を表示する

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

SELECT * FROM pg_stat_progress_create_index;

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

空のテーブルまたは行数が不十分なテーブルの遅延インデックスを作成する

デフォルトでは、空のテーブルや、num_leaves インデックス オプションの値よりも行数が少ないテーブルに ScaNN インデックスを作成することはできません。

この制限を回避するには、遅延インデックス作成を有効にして、AlloyDB Omni がテーブル内の行数が num_leaves で定義されたしきい値に達するまでインデックス作成を遅延できるようにします。しきい値に達すると、AlloyDB Omni はバックグラウンドでインデックスのビルドを開始します。

この遅延オペレーションは非ブロッキング プロセスであるため、読み取りや書き込みなどの他のデータベース オペレーションは中断されずに続行されます。インデックスの再構築はバックグラウンドで行われるため、テーブルが小さなバッチでデータ行を取り込む場合は、遅延インデックス作成が適しています。インデックスの再構築は、行数がしきい値に達すると自動的にトリガーされます。

ただし、1 つのトランザクションでテーブルに大量の行を挿入する場合は、トランザクションを複数のトランザクションに分割するか、遅延インデックス作成を有効にせずに ScaNN インデックスを生成することをおすすめします。

遅延インデックス作成を有効にする

遅延インデックス作成を有効にする手順は次のとおりです。

  1. scann.enable_index_maintenance フラグと次のいずれかのフラグが有効になっていることを確認します。

    使用状況を構成するには、gcloud CLI を使用します。

    • 他のプレビュー機能とともに遅延インデックス作成を有効にするには:

      gcloud alloydb instances update INSTANCE_ID \
         --database-flags scann.enable_index_maintenance=on \
         --database-flags scann.enable_preview_features=on \
         --region=REGION_ID \
         --cluster=CLUSTER_ID \
         --project=PROJECT_ID
      
    • 他のプレビュー機能を有効にせずに、遅延インデックス作成を明示的に有効にするには:

      gcloud alloydb instances update INSTANCE_ID \
         --database-flags scann.enable_index_maintenance=on \
         --database-flags scann.enable_index_with_insufficient_data=on \
         --region=REGION_ID \
         --cluster=CLUSTER_ID \
         --project=PROJECT_ID
      

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

    • INSTANCE_ID: インスタンスの ID。
    • REGION_ID: インスタンスが配置されているリージョン(例: us-central1)。
    • CLUSTER_ID: インスタンスが配置されているクラスタの ID。
    • PROJECT_ID: クラスタが配置されているプロジェクトの ID。
  2. ScaNN インデックスを作成します。手動モードでインデックスを作成する場合は、auto_maintenance パラメータが on に設定されていることを確認してください。詳細については、手動で調整されたインデックスを作成するをご覧ください。

制限事項

  • 自動インデックス作成のバックグラウンド プロセスでは、データベース レベルのフラグ値が使用されます。SET LOCAL コマンドを使用してセッションレベルのフラグを設定した場合でも、プロセスではデータベース レベルで設定されたフラグ値が考慮されます。
  • 大量のデータを 1 回のトランザクションで空のテーブルに一括挿入する場合は、単一の挿入トランザクションを実行してから ScaNN インデックスを作成することをおすすめします。

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

AlloyDB Omni は、次の理由で、空のテーブルまたは行数が非常に少ないテーブルに ScaNN インデックスが作成されないように検証を使用します。

  • ScaNN インデックスのトレーニングに使用されるデータが不十分です。これにより、ベクトル類似度検索の再現率が低下する可能性があります。

  • データベースへの書き込みパフォーマンスが低下する可能性があります。

パフォーマンスが最適でない場合は、インデックスの作成を延期することをおすすめします。

ただし、開発やテストのシナリオによっては、空のテーブルや小さなテーブルにインデックスを作成することが必要な場合があります。このような場合は、インデックスの作成を強制できます。インデックスの作成を強制するには、SUPERUSER 権限が必要です。

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

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

    SET scann.allow_blocked_operations = true;
    
  2. これらのクエリの実行に使用するユーザーに SUPERUSER 権限がない場合は、割り当てます。

    CREATE USER USERNAME WITH SUPERUSER PASSWORD PASSWORD;
    

    次の変数を置き換えます。

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

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

AlloyDB Omni は、データセットと、インデックスをすばやく構築するために選択したインデックスのタイプに応じて、複数の並列ワーカーを自動的に生成する場合があります。これは、3 レベルまたは 4 レベルの ScaNN インデックスを作成する場合や、データセットが 1 億行を超える場合にトリガーされます。

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

ScaNN インデックスの作成時にメモリ不足の問題が発生しないように、maintenance_work_mem データベース フラグと shared_buffers データベース フラグがマシンのメモリの合計より小さい値に設定されていることを確認してください。

クエリの実行

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

テキスト文字列に最も近いセマンティック ネイバーを見つけるには、google_ml.embedding() 関数を使用してテキストをベクトルに変換します。

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

SELECT * FROM TABLE
ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY
  google_ml.embedding(
      model_id => 'MODEL_ID',
      content => 'CONTENT')::vector
LIMIT ROW_COUNT

次の変数を置き換えます。

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

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

  • DISTANCE_FUNCTION_QUERY: このクエリで使用する距離関数。インデックスの作成時に使用した距離関数に対応するクエリを選択します。

    • L2 距離: <->

    • 内積: <#>

    • コサイン距離: <=>

  • MODEL_ID: 使用する登録済みエンベディング モデルの ID。

  • CONTENT: エンベディングに変換して検索するテキスト文字列。

  • ROW_COUNT: 返される行数。たとえば、最も適合する 1 件のみを取得したい場合は、1 を指定します。

次のステップ