このページでは、保存されたエンベディングを使用してインデックスを生成し、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.zero_knob_index_creation を有効にします。フラグを有効にしたら、次のコマンドを実行します。
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: インデックスのビルド時間と検索パフォーマンスのバランスが取れたインデックスを作成します。
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 ~ 1048576 の任意の値に設定します。QUANTIZER: 使用する量子化方式のタイプ。使用できるオプションは次のとおりです。SQ8: クエリのパフォーマンスと再現率の損失のバランスを取ります。通常、再現率の損失は 1 ~ 2% 未満です。これはデフォルト値です。AH: カラム型エンジンが有効になっており、インデックスとテーブルのデータが構成されたサイズに従ってカラム型エンジンに読み込まれている場合は、クエリのパフォーマンスが向上する可能性があります。AHはSQ8と比較して最大 4 倍圧縮されます。詳細については、ScaNN のチューニングに関するベスト プラクティスをご覧ください。FLAT: 検索パフォーマンスを犠牲にして、99% 以上の再現率を実現します。
MAX_NUM_LEVELS: K 平均法クラスタリング ツリーのレベルの最大数。2 レベルのツリーベースの量子化の場合は1(デフォルト)、3 レベルのツリーベースの量子化の場合は2に設定します。
他のインデックス作成またはクエリ ランタイム パラメータを追加して、インデックスを調整できます。詳細については、ScaNN インデックスをチューニングするをご覧ください。
既存のインデックスのモードを変更する
AUTO モードを使用して ScaNN インデックスを作成し、インデックスを手動でチューニングする場合は、モードを MANUAL に変更する必要があります。
モードを MANUAL に変更する手順は次のとおりです。
インデックスを更新して、モードを
MANUALに設定します。ALTER INDEX INDEX_NAME SET (mode = 'MANUAL', num_leaves = NUM_LEAVES_VALUE);次のように置き換えます。
INDEX_NAME: 作成するインデックスの名前(例:my-scann-index)。インデックス名はデータベース全体で共有されます。各インデックス名がデータベース内の各テーブルで一意であることを確認します。NUM_LEAVES_VALUE: このインデックスに適用するパーティションの数。1 ~ 1048576 の任意の値に設定します。
他のインデックス作成またはクエリ ランタイム パラメータを追加して、インデックスを調整できます。詳細については、
ScaNNインデックスをチューニングするをご覧ください。インデックスを再構築してパラメータを適用します。
REINDEX INDEX CONCURRENTLY INDEX_NAME;
モードを AUTO に変更する手順は次のとおりです。
インデックスを更新して、モードを
AUTOに設定します。ALTER INDEX INDEX_NAME SET (mode = 'AUTO');インデックスを再構築してパラメータを適用します。
REINDEX INDEX CONCURRENTLY INDEX_NAME;
特定のパラメータを使用して 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~1048576 の任意の値に設定します。この値を決定する方法の詳細については、ScaNNインデックスをチューニングするをご覧ください。QUANTIZER: 使用する量子化方式のタイプ。使用できるオプションは次のとおりです。SQ8: クエリのパフォーマンスと再現率の損失のバランスを取ります。通常、再現率の損失は 1 ~ 2% 未満です。これはデフォルト値です。AH: カラム型エンジンが有効になっており、インデックスとテーブルのデータが構成されたサイズに従ってカラム型エンジンに読み込まれている場合は、クエリのパフォーマンスが向上する可能性があります。AHはSQ8と比較して最大 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 は、データセットと、インデックスをすばやく構築するために選択したインデックスのタイプに応じて、複数の並列ワーカーを自動的に生成する場合があります。
並列インデックス構築は、3 レベルの ScaNN インデックスを作成する場合や、データセットが 1 億行を超える場合にトリガーされます。
AlloyDB は並列ワーカーの数を自動的に最適化しますが、max_parallel_maintenance_workers、max_parallel_workers、min_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-indexEMBEDDING_COLUMN: 保存されたエンベディングを含む列。DISTANCE_FUNCTION_QUERY: このクエリで使用する距離関数。インデックスの作成時に使用した距離関数に基づいて、次のいずれかを選択します。L2 距離:
<->内積:
<#>コサイン距離:
<=>
EMBEDDING: 保存されているセマンティック ネイバーの中で最も近いものを見つけるエンベディング ベクトル。ROW_COUNT: 返される行数。最も適合するものが 1 つだけ必要な場合は、
1を指定します。
embedding() 関数を使用してテキストをベクトルに変換することもできます。embedding() は real 配列を返すため、最近傍演算子(embedding()vector<->(L2 距離の場合)。これらの演算子は、ScaNN インデックスを使用して、意味的に最も類似したエンベディングを含むデータベース行を見つけることができます。
次のステップ
- ベクトル類似性検索を実行する
- ベクトルクエリのパフォーマンスを調整する
- ベクトル インデックス指標
- AlloyDB、pgvector、モデル エンドポイント管理を使用してスマート ショッピング アシスタントを構築する方法について学習する。