ベクトル インデックスを管理する

この機能に関するフィードバックやサポートのリクエストを行う場合は、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)ステートメントを使用します。

  1. [BigQuery] ページに移動します。

    [BigQuery] に移動

  2. クエリエディタで、次の 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: このインデックスを使用してベクトル検索を行う際に使用するデフォルトの距離の種類を指定します。サポートされている値は EUCLIDEANCOSINE です。デフォルトは EUCLIDEAN です。

      インデックスの作成自体では、トレーニングに常に EUCLIDEAN 距離を使用しますが、VECTOR_SEARCH 関数で使用される距離は異なる場合があります。

      VECTOR_SEARCH 関数の distance_type 引数に値を指定すると、DISTANCE_TYPE 値の代わりにその値が使用されます。

    • NUM_LISTS: IVF アルゴリズムが作成するリストの数を決定する、5,000 以下の INT64 値。IVF アルゴリズムは、データ空間全体を NUM_LISTS に等しい複数のリストに分割します。データポイントが互いに近いほど、同じリストに入る可能性が高くなります。NUM_LISTS が小さい場合、リスト数が少なくなってそれぞれのデータポイント数が多くなり、値が大きい場合、リスト数が多くなってそれぞれのデータポイント数が少なくなります。

      NUM_LISTSVECTOR_SEARCH 関数の fractions_list_to_search 引数と組み合わせて使用すると、効率的なベクトル検索を作成できます。エンベディング空間内の多数の小さなグループにデータが分散している場合、NUM_LISTS に大きな値を指定することでより多くのリストを持つインデックスを作成し、fractions_list_to_search により小さな値を指定することでベクトル検索でスキャンするリストの数を減らします。データが少数の大きなグループに分散している場合は、NUM_LISTS に小さい値を、fractions_list_to_search に大きい値を使用します。num_lists の値が大きいと、ベクトル インデックスの構築に時間がかかることがあります。

      NUM_LISTS を指定しない場合、BigQuery によって適切な値が計算されます。

次の例では、my_tableembedding 列にベクトル インデックスを作成します。

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_tableembedding 列にベクトル インデックスを作成し、使用する距離の種類と 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_typeBACKGROUND に設定して、プロジェクトを予約に割り当てます。

SQL

CREATE ASSIGNMENT DDL ステートメントを使用します。

  1. Google Cloud コンソールで [BigQuery] ページに移動します。

    [BigQuery] に移動

  2. クエリエディタで次のステートメントを入力します。

    CREATE ASSIGNMENT
      `ADMIN_PROJECT_ID.region-LOCATION.RESERVATION_NAME.ASSIGNMENT_ID`
    OPTIONS (
      assignee = 'projects/PROJECT_ID',
      job_type = 'BACKGROUND');
    

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

    • ADMIN_PROJECT_ID: 予約リソースを所有する管理プロジェクトのプロジェクト ID
    • LOCATION: 予約のロケーション
    • RESERVATION_NAME: 予約の名前
    • ASSIGNMENT_ID: 割り当ての ID

      ID はプロジェクトとロケーションごとに一意でなければならず、先頭と末尾を英小文字または数字にする必要があり、英小文字、数字、ダッシュのみを使用できます。

    • PROJECT_ID: インデックスを付けるテーブルを含むプロジェクトの ID。このプロジェクトは予約に割り当てられます。

  3. [実行] をクリックします。

クエリの実行方法については、インタラクティブ クエリを実行するをご覧ください。

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

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

  • ADMIN_PROJECT_ID: 予約リソースを所有する管理プロジェクトのプロジェクト ID
  • LOCATION: 予約のロケーション
  • RESERVATION_NAME: 予約の名前
  • PROJECT_ID: この予約に割り当てられるプロジェクトの ID

インデックス ジョブの表示

単一のテーブルでインデックスが作成または更新されるたびに、新しいインデックス ジョブが作成されます。ジョブに関する情報を表示するには、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;

インデックスのあるテーブルが削除されると、そのインデックスは自動的に削除されます。

次のステップ