ScaNN インデックスは、Google 製の近似最近傍検索用のツリーベースの量子化インデックスです。HNSW と比較して、インデックスの構築時間が短く、メモリ使用量も小さくなります。また、ワークロードに応じて HNSW よりも QPS が速くなります。
始める前に
インデックスの作成を開始する前に、以下の前提条件を整える必要があります。
AlloyDB Omni データベースのテーブルにエンベディング ベクトルが追加されている。
空のテーブルやパーティション分割テーブルに ScaNN インデックスを作成しようとすると、問題が発生することがあります。生成されたエラーの詳細については、ScaNN インデックス エラーのトラブルシューティングをご覧ください。空のテーブルまたは小さいテーブルにインデックスを作成するには、空のテーブルまたはほぼ空のテーブルのインデックス作成を延期するをご覧ください。
vector拡張機能とalloydb_scann拡張機能がインストールされている場合:CREATE EXTENSION IF NOT EXISTS alloydb_scann CASCADE;alloydb_scann拡張機能をインストールすると、vector拡張機能がインストールされているかどうかが自動的に確認され、インストールされていない場合はインストールされます。vectorを別途手動でインストールする必要はありません。4 レベルの ScaNN インデックスを作成する場合は、まず AlloyDB Omni インスタンスのプレビュー機能を有効にする必要があります。プレビュー機能を有効にするには、次のいずれかの方法を選択します。
scann.enable_preview_featuresデータベース フラグを有効にします。データベース フラグの構成の詳細については、データベース フラグを構成するをご覧ください。
セッション レベルまたはインスタンス レベルで
scann.max_allowed_num_levelsデータベース フラグを3に設定します。セッション レベルでフラグを設定するには、次のコマンドを実行します。SET scann.max_allowed_num_levels = 3;インスタンス レベルでフラグを設定するには、
--database-flagsフィールドを使用してgcloud alloydb alloydb instances updateを実行します。
自動的にチューニングされるインデックスを作成する
自動的にチューニングされる 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
- L2 距離:
-
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
- L2 距離:
-
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
- L2 距離:
-
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 レベルのインデックスを作成します。
手動でチューニングされたインデックスを自動でチューニングされたインデックスに変換する
手動で調整されたインデックスを自動で調整されたインデックスに変換する手順は次のとおりです。
手動で調整したインデックスに定義されているすべてのクエリ パラメータをリセットします。
ALTER INDEX INDEX_NAME RESET (PARAMETER_NAME);次の変数を置き換えます。
INDEX_NAME: 変換するインデックスの名前。例:my_scann_indexインデックス名はデータベース全体で共有されます。各インデックス名がデータベース内の各テーブルで一意であることを確認します。PARAMETER_NAME: リセットするクエリ パラメータの名前を含むカンマ区切りのリスト。例:num_leaves, quantizationnum_leavesをリセットする前に、他のすべてのクエリ パラメータをリセットする必要があります。
手動で調整したインデックスを再作成して、自動調整されたインデックスに変換します。
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 インデックスを生成することをおすすめします。
遅延インデックス作成を有効にする
遅延インデックス作成を有効にする手順は次のとおりです。
scann.enable_index_maintenanceフラグと次のいずれかのフラグが有効になっていることを確認します。scann.enable_preview_features: このフラグは、他のプレビュー機能も有効にします。scann.enable_index_with_insufficient_data: このフラグは、遅延インデックス作成機能のみを明示的に有効にします。
使用状況を構成するには、
gcloudCLI を使用します。他のプレビュー機能とともに遅延インデックス作成を有効にするには:
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。
ScaNN インデックスを作成します。手動モードでインデックスを作成する場合は、
auto_maintenanceパラメータがonに設定されていることを確認してください。詳細については、手動で調整されたインデックスを作成するをご覧ください。
制限事項
- 自動インデックス作成のバックグラウンド プロセスでは、データベース レベルのフラグ値が使用されます。
SET LOCALコマンドを使用してセッションレベルのフラグを設定した場合でも、プロセスではデータベース レベルで設定されたフラグ値が考慮されます。 - 大量のデータを 1 回のトランザクションで空のテーブルに一括挿入する場合は、単一の挿入トランザクションを実行してから ScaNN インデックスを作成することをおすすめします。
空のテーブルまたは小さなテーブルでインデックスの作成を強制する
AlloyDB Omni は、次の理由で、空のテーブルまたは行数が非常に少ないテーブルに ScaNN インデックスが作成されないように検証を使用します。
ScaNN インデックスのトレーニングに使用されるデータが不十分です。これにより、ベクトル類似度検索の再現率が低下する可能性があります。
データベースへの書き込みパフォーマンスが低下する可能性があります。
パフォーマンスが最適でない場合は、インデックスの作成を延期することをおすすめします。
ただし、開発やテストのシナリオによっては、空のテーブルや小さなテーブルにインデックスを作成することが必要な場合があります。このような場合は、インデックスの作成を強制できます。インデックスの作成を強制するには、SUPERUSER 権限が必要です。
インデックスの作成を強制するには、次の操作を行います。
データベースで
scann.allow_blocked_operationsセッション レベルのパラメータをtrueに設定します。SET scann.allow_blocked_operations = true;これらのクエリの実行に使用するユーザーに
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を指定します。
次のステップ
- ベクトル類似性検索を実行する
- ベクトルクエリのパフォーマンスを調整する
- ベクトル インデックス指標
- AlloyDB、pgvector、モデル エンドポイント管理を使用してスマート ショッピング アシスタントを構築する方法について学習する。