ベクトル インデックスを管理する
この機能に関するフィードバックやサポートのリクエストを行う場合は、bq-vector-search@google.com 宛てにメールを送信してください。
このドキュメントでは、ベクトル インデックスを作成および管理する方法について説明します。
ベクトル インデックスは、VECTOR_SEARCH
関数でエンベディングのより効率的なベクトル検索を実行できるようにするために設計されたデータ構造です。VECTOR_SEARCH
でベクトル インデックスを使用できる場合、この関数は近似最近傍検索手法を使用して検索のパフォーマンスを向上させます。ただし、再現率を低減し、より近似的な結果を返すというトレードオフがあります。
ロールと権限
ベクトル インデックスを作成するには、インデックスを作成するテーブルに対する bigquery.tables.createIndex
IAM 権限が必要です。ベクトル インデックスを削除するには、bigquery.tables.deleteIndex
権限が必要です。次の IAM 事前定義ロールには、ベクトル インデックスの操作に必要な権限が含まれています。
- BigQuery データオーナー(
roles/bigquery.dataOwner
) - BigQuery データ編集者(
roles/bigquery.dataEditor
)
ベクトル インデックスを作成する
ベクトル インデックスを作成するには、CREATE VECTOR INDEX
データ定義言語(DDL)ステートメントを使用します。
[BigQuery] ページに移動します。
クエリエディタで、次の SQL ステートメントを実行します。
CREATE [ OR REPLACE ] VECTOR INDEX [ IF NOT EXISTS ] INDEX_NAME ON DATASET_NAME.TABLE_NAME(COLUMN_NAME) OPTIONS(index_type = INDEX_TYPE, distance_type = DISTANCE_TYPE, ivf_options = '{"num_lists":NUM_LISTS}')
次のように置き換えます。
INDEX_NAME
: 作成するベクトル インデックスの名前。インデックスは常にベーステーブルと同じプロジェクトとデータセットに作成されるため、名前にこれらを指定する必要はありません。DATASET_NAME
: テーブルを含むデータセットの名前。TABLE_NAME
: エンベディング データを持つ列を含むテーブルの名前。COLUMN_NAME
: エンベディング データを含む列の名前。列の型はARRAY<FLOAT64>
にする必要があります。この列に子フィールドを含めることはできません。配列内のすべての要素は非NULL
でなければならず、列の値はすべて同じ配列ディメンションでなければなりません。INDEX_TYPE
: ベクトル インデックスの作成に使用するアルゴリズム。サポートされる値はIVF
のみです。IVF
を指定すると、ベクトル インデックスが反転ファイル インデックス(IVF)としてビルドされます。IVF は K 平均法アルゴリズムを使用してベクトルデータをクラスタ化し、それらのクラスタに基づいてベクトルデータを分割します。VECTOR_SEARCH
関数を使用してベクトルデータを検索する場合、これらのパーティションを使用することで、判別するために読み取る必要があるデータの量を削減できます。DISTANCE_TYPE
: このインデックスを使用してベクトル検索を行う際に使用するデフォルトの距離の種類を指定します。サポートされている値はEUCLIDEAN
、COSINE
です。デフォルトはEUCLIDEAN
です。インデックスの作成自体では、トレーニングに常に
EUCLIDEAN
距離を使用しますが、VECTOR_SEARCH
関数で使用される距離は異なる場合があります。VECTOR_SEARCH
関数のdistance_type
引数に値を指定すると、DISTANCE_TYPE
値の代わりにその値が使用されます。NUM_LISTS
: IVF アルゴリズムが作成するリストの数を決定する、5,000 以下のINT64
値。IVF アルゴリズムは、データ空間全体をNUM_LISTS
に等しい複数のリストに分割します。データポイントが互いに近いほど、同じリストに入る可能性が高くなります。NUM_LISTS
が小さい場合、リスト数が少なくなってそれぞれのデータポイント数が多くなり、値が大きい場合、リスト数が多くなってそれぞれのデータポイント数が少なくなります。NUM_LISTS
をVECTOR_SEARCH
関数のfractions_list_to_search
引数と組み合わせて使用すると、効率的なベクトル検索を作成できます。エンベディング空間内の多数の小さなグループにデータが分散している場合、NUM_LISTS
に大きな値を指定することでより多くのリストを持つインデックスを作成し、fractions_list_to_search
により小さな値を指定することでベクトル検索でスキャンするリストの数を減らします。データが少数の大きなグループに分散している場合は、NUM_LISTS
に小さい値を、fractions_list_to_search
に大きい値を使用します。num_lists
の値が大きいと、ベクトル インデックスの構築に時間がかかることがあります。NUM_LISTS
を指定しない場合、BigQuery によって適切な値が計算されます。
次の例では、my_table
の embedding
列にベクトル インデックスを作成します。
CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>); CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding) OPTIONS(index_type = 'IVF');
次の例では、my_table
の embedding
列にベクトル インデックスを作成し、使用する距離の種類と IVF オプションを指定しています。
CREATE TABLE my_dataset.my_table(embedding ARRAY<FLOAT64>); CREATE VECTOR INDEX my_index ON my_dataset.my_table(embedding) OPTIONS(index_type = 'IVF', distance_type = 'COSINE', ivf_options = '{"num_lists": 2500}')
インデックスの更新について
ベクトル インデックスは BigQuery によって完全に管理され、インデックス付きテーブルが変更されると自動的に更新されます。テーブル内のインデックス付き列を削除するか、テーブル自体の名前を変更すると、ベクトル インデックスは自動的に削除されます。
10 MB 未満のテーブルにベクトル インデックスを作成すると、ベクトル インデックスにはデータが挿入されません。同様に、インデックス付きテーブルからデータを削除してテーブルサイズが 10 MB を下回るようになった場合、ベクトル インデックスは一時的に無効になります。この場合、ベクトル検索クエリではインデックスが使用されず、Job
リソースの vectorSearchStatistics
セクションにある indexUnusedReasons
コードは BASE_TABLE_TOO_SMALL
になります。インデックスがない場合、VECTOR_SEARCH
は自動的にブルート フォースを使用してエンベディングの最近傍を検出します。
ベクトル インデックスに関する情報を取得する
INFORMATION_SCHEMA
をクエリすることで、ベクトル インデックスの存在と準備状況を確認できます。次のビューには、ベクトル インデックスのメタデータが含まれます。
INFORMATION_SCHEMA.VECTOR_INDEXES
ビューには、データセット内のベクトル インデックスに関する情報が含まれます。CREATE VECTOR INDEX
ステートメントが完了した後も、インデックスを使用するにはインデックスにデータが入力されている必要があります。last_refresh_time
列とcoverage_percentage
列を使用して、ベクトル インデックスの準備状況を確認できます。ベクトル インデックスの準備ができていない場合でも、テーブルに対してVECTOR_SEARCH
関数を使用できますが、インデックスがない場合には実行速度が遅くなる可能性があります。INFORMATION_SCHEMA.VECTOR_INDEX_COLUMNS
ビューには、データセット内のすべてのテーブルについてのベクトル インデックス付き列に関する情報が含まれます。INFORMATION_SCHEMA.VECTOR_INDEX_OPTIONS
ビューには、データセット内のベクトル インデックスで使用されているオプションに関する情報が含まれています。
ベクトル インデックスの例
次の例では、プロジェクト my_project
にあるデータセット my_dataset
内のテーブルに対するアクティブなベクトル インデックスがすべて表示されます。これには、名前、作成に使用された DDL ステートメント、カバレッジの割合が含まれます。インデックス付きベーステーブルが 10 MB 未満の場合、そのインデックスにはデータが取り込まれず、coverage_percentage
値は 0 になります。
SELECT table_name, index_name, ddl, coverage_percentage FROM my_project.my_dataset.INFORMATION_SCHEMA.VECTOR_INDEXES WHERE index_status = 'ACTIVE';
次のような結果になります。
+-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+ | table_name | index_name | ddl | coverage_percentage | +-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+ | small_table | myindex1 | CREATE VECTOR INDEX `myindex1` ON `my_project.my_dataset.small_table`(embeddings) | 100 | | | | OPTIONS (distance_type = 'EUCLIDEAN', index_type = 'IVF', ivf_options = '{"numLists": 3}') | | +-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+ | large_table | myindex2 | CREATE VECTOR INDEX `myindex2` ON `my_project.my_dataset.large_table`(vectors) | 42 | | | | OPTIONS (distance_type = 'EUCLIDEAN', index_type = 'IVF', ivf_options = '{"numLists": 12}') | | +-------------+-------------+-----------------------------------------------------------------------------------------------+---------------------+
ベクトル インデックス列の例
次のクエリは、ベクトル インデックスを持つ列の情報を抽出します。
SELECT table_name, index_name, index_column_name, index_field_path FROM my_project.dataset.INFORMATION_SCHEMA.VECTOR_INDEX_COLUMNS;
次のような結果になります。
+------------+------------+-------------------+------------------+ | table_name | index_name | index_column_name | index_field_path | +------------+------------+-------------------+------------------+ | table1 | indexa | a | a | | table2 | indexb | b | b | | table3 | indexc | c | c | +------------+------------+-------------------+------------------+
ベクトル インデックス オプションの例
次のクエリは、ベクトル インデックス オプションに関する情報を抽出します。
SELECT table_name, index_name, option_name, option_type, option_value FROM my_project.dataset.INFORMATION_SCHEMA.VECTOR_INDEX_OPTIONS;
次のような結果になります。
+------------+------------+------------------+------------------+--------------------+ | table_name | index_name | option_name | option_type | option_value | +------------+------------+------------------+------------------+--------------------+ | table1 | indexa | distance_type | STRING | EUCLIDEAN | | table1 | indexa | index_type | STRING | IVF | | table2 | indexb | ivf_options | STRING | {"num_lists": 100} | | table2 | indexb | index_type | STRING | IVF | +------------+------------+------------------+------------------+--------------------+
ベクトル インデックスの使用
ベクトル インデックスの使用状況に関する情報は、ベクトル検索クエリを実行したジョブのジョブ メタデータで確認できます。ジョブ メタデータを表示するには、Google Cloud コンソール、bq コマンドライン ツール、BigQuery API、またはクライアント ライブラリを使用します。
Google Cloud コンソールを使用する場合、[ベクター インデックス使用モード] フィールドと [ベクター インデックスが使用されない理由] フィールドにベクトル インデックスの使用状況に関する情報が表示されます。
bq ツールまたは BigQuery API を使用する場合は、Job
リソースの VectorSearchStatistics
セクションでベクトル インデックスの使用状況に関する情報を確認できます。
インデックス使用モードは、次のいずれかの値により、ベクトル インデックスが使用されたかどうかを示します。
UNUSED
: ベクトル インデックスは使用されていません。PARTIALLY_USED
: クエリ内のVECTOR_SEARCH
関数の中には、ベクトル インデックスを使用したものと使用しなかったものがあります。FULLY_USED
: クエリ内のすべてのVECTOR_SEARCH
関数でベクトル インデックスが使用されました。
インデックス使用モードの値が UNUSED
または PARTIALLY_USED
の場合、「インデックスが使用されない理由」にクエリでベクトル インデックスが使用されなかった理由が示されます。
たとえば、bq show --format=prettyjson -j my_job_id
から返された次の結果は、インデックスが使用されなかった理由が、VECTOR_SEARCH
関数で use_brute_force
オプションが指定されているからであることが示されています。
"vectorSearchStatistics": { "indexUnusedReasons": [ { "baseTable": { "datasetId": "my_dataset", "projectId": "my_project", "tableId": "my_table" }, "code": "INDEX_SUPPRESSED_BY_FUNCTION_OPTION", "message": "No vector index was used for the base table `my_project:my_dataset.my_table` because use_brute_force option has been specified." } ], "indexUsageMode": "UNUSED" }
インデックス管理オプション
インデックスを作成して BigQuery にそれらを維持させるには、次の 2 つの方法があります。
- デフォルトの共有スロットプールを使用する: インデックスが付く予定のデータが組織ごとの上限を下回っている場合は、インデックス管理用に無料の共有スロットプールを使用できます。
- 独自の予約を使用する: 大規模な本番環境ワークロードでより予測可能かつ一貫したインデックス付けを進めるため、インデックス管理用に独自の予約を使用できます。
共有スロットを使用する
インデックス専用の予約を使用するようプロジェクトをまだ構成していない場合、インデックス管理は次の制約に従い、無料の共有スロットプールで処理されます。
テーブルにデータを追加した結果としてインデックス付きテーブルの合計サイズが組織の上限を超えた場合、BigQuery はすべてのインデックス付きテーブルに関するインデックス管理を一時停止します。この場合、INFORMATION_SCHEMA.VECTOR_INDEXES
ビューの index_status
フィールドに PENDING DISABLEMENT
が表示され、インデックスは削除対象のキューに入ります。インデックスの無効化が保留状態になっている間、そのインデックスは引き続きクエリで使用され、インデックス ストレージに対して料金が発生します。インデックスが削除されると、index_status
フィールドにはインデックスが TEMPORARILY DISABLED
として表示されます。この状態では、クエリはインデックスを使用せず、インデックス ストレージには課金されません。この場合、IndexUnusedReason
コードは BASE_TABLE_TOO_LARGE
です。
テーブルからデータを削除した結果、インデックス付きテーブルの合計サイズが組織ごとの上限を下回ると、すべてのインデックス付きテーブルのインデックス管理が再開します。INFORMATION_SCHEMA.VECTOR_INDEXES
ビューの index_status
フィールドは ACTIVE
になり、クエリでインデックスを使用でき、インデックス ストレージに対して課金されます。
BigQuery は、共有プールの使用可能容量や観察されるインデックス処理スループットを保証することはありません。本番環境アプリケーションでは、インデックス処理に専用スロットを使用することをおすすめします。
独自の予約を使用する
デフォルトの共有スロットプールを使用する代わりに、必要に応じて、独自の予約を指定してテーブルにインデックスを付けることもできます。独自の予約を使用すると、作成、更新、バックグラウンド最適化などのインデックス管理ジョブのパフォーマンスが予測可能で一貫したものになります。
- 予約でインデックス ジョブが実行されるときには、テーブルサイズの上限がありません。
- 独自の予約を使用すると、インデックスを柔軟に管理できます。非常に大きなインデックスを作成する必要がある場合、またはインデックス付きテーブルを大幅に更新する必要がある場合には、より多くのスロットを一時的に割り当てに追加できます。
指定された予約を使用してプロジェクト内のテーブルにインデックスを付けるには、テーブルが配置されているリージョンで予約を作成します。次に、job_type
を BACKGROUND
に設定して、プロジェクトを予約に割り当てます。
SQL
CREATE ASSIGNMENT
DDL ステートメントを使用します。
Google Cloud コンソールで [BigQuery] ページに移動します。
クエリエディタで次のステートメントを入力します。
CREATE ASSIGNMENT `ADMIN_PROJECT_ID.region-LOCATION.RESERVATION_NAME.ASSIGNMENT_ID` OPTIONS ( assignee = 'projects/PROJECT_ID', job_type = 'BACKGROUND');
次のように置き換えます。
[
実行] をクリックします。
クエリの実行方法については、インタラクティブ クエリを実行するをご覧ください。
bq
bq mk
コマンドを使用します。
bq mk \ --project_id=ADMIN_PROJECT_ID \ --location=LOCATION \ --reservation_assignment \ --reservation_id=RESERVATION_NAME \ --assignee_id=PROJECT_ID \ --job_type=BACKGROUND \ --assignee_type=PROJECT
次のように置き換えます。
インデックス ジョブの表示
単一のテーブルでインデックスが作成または更新されるたびに、新しいインデックス ジョブが作成されます。ジョブに関する情報を表示するには、INFORMATION_SCHEMA.JOBS*
ビューをクエリします。クエリの WHERE
句で job_type IS NULL AND SEARCH(job_id, '`search_index`')
を設定すると、インデックス ジョブをフィルタリングできます。次の例では、プロジェクト my_project
内の最新の 5 つのインデックス ジョブが一覧表示されます。
SELECT * FROM region-us.INFORMATION_SCHEMA.JOBS WHERE project_id = 'my_project' AND job_type IS NULL AND SEARCH(job_id, '`search_index`') ORDER BY creation_time DESC LIMIT 5;
予約サイズの選択
予約に適切なスロット数を選択するには、インデックス管理ジョブが実行されるタイミング、使用されるスロット数、時間の経過に伴う使用状況の推移を考慮する必要があります。BigQuery は、次のようなときにインデックス管理ジョブをトリガーします。
- テーブルにインデックスを作成したとき。
- インデックス付きテーブルでデータが変更されたとき。
- テーブルのスキーマが変更された影響として、どの列がインデックスに登録されるかが変化したとき。
- インデックスのデータとメタデータが定期的に最適化または更新されるとき。
テーブルに対するインデックス管理ジョブに必要なスロット数は、次の要因に依存します。
- テーブルのサイズ
- テーブルへのデータ取り込みのレート
- テーブルに適用される DML ステートメントのレート
- インデックスを構築および維持するときに許容される遅延
- インデックスの複雑さ(通常は、重複する語の数などのデータ属性によって決まります)
使用状況と進捗状況のモニタリング
インデックス管理ジョブを効率的に実行するのに必要なスロット数を評価する最良の方法は、スロットの使用状況をモニタリングし、それに応じて予約サイズを調整することです。次のクエリは、インデックス管理ジョブの日次スロット使用状況を生成します。リージョン us-west1
では過去 30 日間のみが含まれます。
SELECT TIMESTAMP_TRUNC(job.creation_time, DAY) AS usage_date, -- Aggregate total_slots_ms used for index-management jobs in a day and divide -- by the number of milliseconds in a day. This value is most accurate for -- days with consistent slot usage. SAFE_DIVIDE(SUM(job.total_slot_ms), (1000 * 60 * 60 * 24)) AS average_daily_slot_usage FROM `region-us-west1`.INFORMATION_SCHEMA.JOBS job WHERE project_id = 'my_project' AND job_type IS NULL AND SEARCH(job_id, '`search_index`') GROUP BY usage_date ORDER BY usage_date DESC limit 30;
インデックス管理ジョブを実行するための十分なスロットがない場合、インデックスがテーブルと非同期状態になってインデックス ジョブが失敗することがあります。その場合、BigQuery はインデックスをまったくゼロの状態から再構築します。非同期インデックスを回避するには、データ取り込みと最適化によるインデックスの更新をサポートできるような十分なスロットがあることを確認してください。スロット使用状況のモニタリングの詳細については、管理リソースグラフをご覧ください。
ベクトル インデックスを削除する
ベクトル インデックスが不要になった場合や、インデックスを付けるテーブル上の列を変更する場合は、DROP VECTOR INDEX
DDL ステートメントを使用して、そのテーブルのインデックスを削除できます。
例:
DROP VECTOR INDEX my_index ON my_dataset.indexed_table;
インデックスのあるテーブルが削除されると、そのインデックスは自動的に削除されます。
次のステップ
- ベクトル インデックスのユースケース、料金、制限事項の概要については、ベクトル検索の概要をご覧ください。
- ベクトル検索を使用したエンベディングの検索のチュートリアルをお試しください。