管理矢量索引

如需对此功能提供反馈或请求支持,请发送电子邮件至 bq-vector-search@google.com

本文档介绍如何创建和管理矢量索引。

矢量索引是一种数据结构,旨在让 VECTOR_SEARCH 函数对嵌入执行更高效的矢量搜索。当 VECTOR_SEARCH 能够使用矢量索引时,该函数会使用近似最近邻搜索技术来帮助提高搜索性能,虽然降低召回率,但可返回更接近的结果。

角色与权限

要创建矢量索引,您需要对要在其中创建索引的表拥有 bigquery.tables.createIndex IAM 权限。要删除矢量索引,您需要拥有 bigquery.tables.deleteIndex 权限。以下每个预定义的 IAM 角色都包含使用矢量索引所需的权限:

  • BigQuery Data Owner (roles/bigquery.dataOwner)
  • BigQuery Data Editor (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-means 算法对矢量数据进行聚类,然后根据这些聚类对矢量数据进行分区。当您使用 VECTOR_SEARCH 函数搜索矢量数据时,它可以使用这些分区来减少为确定结果而需要读取的数据量。
    • DISTANCE_TYPE:指定使用此索引执行矢量搜索时要使用的默认距离类型。支持的值为 EUCLIDEANCOSINE。 默认值为 EUCLIDEAN

      索引创建本身始终使用 EUCLIDEAN 距离进行训练,但 VECTOR_SEARCH 函数中使用的距离可能不同。

      如果您为 VECTOR_SEARCH 函数的 distance_type 参数指定值,则系统会使用该值(而非 DISTANCE_TYPE 值)。

    • NUM_LISTS:小于或等于 5,000 的 INT64 值,用于确定 IVF 算法创建的列表数量。IVF 算法将整个数据空间划分为若干个列表(数量等于 NUM_LISTS),其中彼此相距较近的数据点更有可能被放在同一个列表中。如果 NUM_LISTS 较小,则列表数量会较少,且包含的数据点数量较多;如果数值较大,则创建的列表数量会较多,且包含的数据点数量较少。

      您可以将 NUM_LISTSVECTOR_SEARCH 函数中的 fraction_lists_to_search 参数结合使用,创建高效的矢量搜索。如果您的数据分布在嵌入空间中的多个小组里,指定较大的 NUM_LISTS 可创建包含较多列表的索引;指定较小的 fraction_lists_to_search 值可在矢量搜索中扫描较少的列表。当您的数据分布在数量较少但规模较大的组中时,请使用较小的 NUM_LISTS 和较大的 fraction_lists_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 会自动转为使用暴力破解来查找嵌入的最近邻。

使用 VECTOR_SEARCH 函数的查询始终会返回正确的结果,即使部分数据尚未编入索引也是如此。

获取有关矢量索引的信息

您可以通过查询 INFORMATION_SCHEMA 来验证矢量索引是否存在以及是否就绪。以下视图包含有关矢量索引的元数据:

矢量索引示例

以下示例展示了位于项目 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 函数都使用一个矢量索引。

当索引使用模式值为 UNUSEDPARTIALLY_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 维护它们,您有两种选择:

  • 使用默认共享槽池:当计划编入索引的数据低于每个组织的限制时,您可以使用免费共享槽池来管理索引。
  • 使用您自己的预留:要在较大的生产工作负载上实现更可预测且一致的索引编制进度,您可以使用自己的预留来管理索引。

使用共享槽

如果您尚未将项目配置为使用专用预留来执行索引操作,就会在免费的共享槽池中处理索引管理,但存在以下限制。

如果向表添加数据,从而导致编入索引的表的总大小超过组织的限制,则 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 语句.

  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 中最新的五个索引作业:

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;

如果删除编入索引的表,其索引也会自动删除。

后续步骤