创建 ScaNN 索引

选择文档版本:

使用存储的嵌入生成 ScaNN 向量索引,并使用 AlloyDB Omni 查询嵌入。

ScaNN 索引是 Google 打造的基于树的量化索引,用于近似最近邻搜索。与 HNSW 相比,它可以缩短索引构建时间并减少内存占用。此外,与 HNSW 相比,它可根据工作负载提供更快的 QPS。

准备工作

在开始创建索引之前,您必须完成以下前提条件。

创建自动调整的索引

自动调优的 ScaNN 索引可让 AlloyDB Omni 管理和调优索引结构,从而简化索引创建。如果您需要精细控制索引调整,请创建手动调整的 ScaNN 索引

自动调整的索引可通过以下两种方式进行优化:

  • (默认)以牺牲索引构建时间为代价,换取向量搜索召回率和延迟时间
  • 平衡索引构建时间和搜索性能

如需创建自动调优的 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 索引,请参阅以下命令。

二级树索引

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:用于存储“向量”数据的列。
  • DISTANCE_FUNCTION:要与此索引一起使用的距离函数。选择以下任一选项:
    • L2 距离:l2
    • 点积:dot_product
    • 余弦距离:cosine
  • NUM_LEAVES_VALUE:要应用于此索引的分区数量。设置为介于 1 到 3000 万之间的任意值。如需详细了解如何选择此值,请参阅ScaNN 索引进行调优
  • QUANTIZER:要使用的量化器类型。请注意,ScaNN 索引可以加载到列式引擎中,以进一步加快向量搜索速度。选择以下任一选项:
    • (默认)SQ8:在召回率损失最小的情况下,提供平衡的查询性能。此值通常低于 1-2%。
    • 预览版AH:与 SQ8 相比,非对称哈希 (AH) 的压缩率最高可达 4 倍。当列式引擎处于启用状态,并且索引和表数据已填充到列式引擎中时,考虑使用此函数可能会获得更好的查询性能。 如需了解详情,请参阅 ScaNN 调优最佳实践
    • FLAT:提供最高的召回率(99% 或更高),但会牺牲搜索性能。

三级树索引

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:用于存储“向量”数据的列。
  • DISTANCE_FUNCTION:要与此索引一起使用的距离函数。选择以下任一选项:
    • L2 距离:l2
    • 点积:dot_product
    • 余弦距离:cosine
  • NUM_LEAVES_VALUE:要应用于此索引的分区数量。设置为介于 1 到 3000 万之间的任意值。如需详细了解如何选择此值,请参阅ScaNN 索引进行调优
  • QUANTIZER:要使用的量化器类型。请注意,ScaNN 索引可以加载到列式引擎中,以进一步加快向量搜索速度。选择以下任一选项:
    • (默认)SQ8:在召回率损失最小的情况下,提供平衡的查询性能。此值通常低于 1-2%。
    • 预览版AH:与 SQ8 相比,非对称哈希 (AH) 的压缩率最高可达 4 倍。当列式引擎处于启用状态,并且索引和表数据已填充到列式引擎中时,考虑使用此函数可能会获得更好的查询性能。 如需了解详情,请参阅 ScaNN 调优最佳实践
    • FLAT:提供最高的召回率(99% 或更高),但会牺牲搜索性能。
  • (可选) AUTO_MAINTENANCE: 控制是否启用或停用索引的自动维护。如需详细了解自动维护,请参阅维护向量索引
    • (默认)ON:AlloyDB Omni 会对索引执行自动维护。
    • OFF:AlloyDB Omni 不会对索引执行自动维护。
  • max_num_levels = 2:K-means 聚类树的形心级别数量上限。将此参数设置为 2 可创建三级索引。

四级树索引

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:用于存储“向量”数据的列。
  • DISTANCE_FUNCTION:要与此索引一起使用的距离函数。选择以下任一选项:
    • L2 距离:l2
    • 点积:dot_product
    • 余弦距离:cosine
  • NUM_LEAVES_VALUE:要应用于此索引的分区数量。设置为介于 1 到 3000 万之间的任意值。如需详细了解如何选择此值,请参阅ScaNN 索引进行调优
  • QUANTIZER:要使用的量化器类型。请注意,ScaNN 索引可以加载到列式引擎中,以进一步加快向量搜索速度。选择以下任一选项:
    • (默认)SQ8:在召回率损失最小的情况下,提供平衡的查询性能。此值通常低于 1-2%。
    • 预览版 AH:与 SQ8 相比,非对称哈希 (AH) 的压缩率最高可达 4 倍。当列式引擎处于启用状态,并且索引和表数据已填充到列式引擎中时,考虑使用此函数可能会获得更好的查询性能。 如需了解详情,请参阅 ScaNN 调优最佳实践
    • FLAT:提供最高的召回率(99% 或更高),但会牺牲搜索性能。
  • max_num_levels = 3:K-means 聚类树的形心级别数量上限。将此参数设置为 3 可创建四级索引。

将手动调优的索引转换为自动调优的索引

如需将手动调优的索引转换为自动调优的索引,请完成以下步骤:

  1. 重置为手动调整的索引定义的所有查询参数。

    ALTER INDEX INDEX_NAME RESET (PARAMETER_NAME);
    

    执行以下变量替换操作:

    • INDEX_NAME:您要转换的索引的名称。例如 my_scann_index。索引名称会在整个数据库中共享。确保每个索引名称对数据库中的每个表都是唯一的。

    • PARAMETER_NAME:逗号分隔列表,其中包含您要重置的查询参数的名称。例如 num_leaves, quantization

      请注意,您必须先重置所有其他查询参数,然后才能重置 num_leaves

  2. 重新编制手动调整的索引,以将其转换为自动调整的索引。

    REINDEX INDEX CONCURRENTLY INDEX_NAME;
    

real[] 数据类型创建 ScaNN 索引

如需为使用 real[] 数据类型(而非 vector)的嵌入列创建索引,请将该列转换为 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 会在后台开始构建索引。

此延迟操作是一个非阻塞进程,允许其他数据库操作(如读取和写入)继续进行,而不会中断。由于索引重建在后台进行,因此当表以小批量方式注入数据行时,适合使用延迟索引创建。当行数达到阈值后,系统会自动触发索引重建。

不过,如果您计划在单个事务中向表中插入大量行,建议您将事务拆分为多个事务,或者生成 ScaNN 索引而不启用延迟索引创建。

启用延迟索引创建

如需启用延迟索引创建,请按以下步骤操作:

  1. 确保已启用 scann.enable_index_maintenance 标志和以下标志之一:

    如需配置其使用情况,请使用 gcloud CLI:

    • 如需启用延迟索引创建功能以及其他预览版功能,请执行以下操作:

      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。
  2. 创建 ScaNN 索引。如果您在手动模式下创建索引,请确保 auto_maintenance 参数设置为 on。如需了解详情,请参阅创建手动调优的索引

限制

  • 自动创建索引的后台进程使用数据库级标志值。即使您使用 SET LOCAL 命令设置了任何会话级标志,该进程也会考虑在数据库级设置的标志值。
  • 如果您计划在单个事务中将大量数据批量插入到空表中,建议您先运行单个插入事务,然后再创建 ScaNN 索引。

在空表或小型表上强制创建索引

AlloyDB Omni 使用验证功能来防止在空表或行数很少的表上创建 ScaNN 索引,原因如下:

  • ScaNN 索引的训练数据不足。这可能会导致向量相似度搜索的召回率较低。

  • 写入数据库的性能可能会下降。

我们建议您在性能欠佳时延迟创建索引

不过,在某些开发或测试场景中,您可能需要在空表或小型表上创建索引。在这些情况下,您可以强制创建索引。请注意,强制创建索引需要 SUPERUSER 权限。

如需强制创建索引,请完成以下步骤:

  1. 在数据库中将 scann.allow_blocked_operations 会话级参数设置为 true

    SET scann.allow_blocked_operations = true;
    
  2. 如果您用来运行这些查询的用户没有 SUPERUSER 权限,请为其分配该权限:

    CREATE USER USERNAME WITH SUPERUSER PASSWORD PASSWORD;
    

    执行以下变量替换操作:

    • USERNAME:您要向其授予 SUPERUSER 权限的用户的名称。
    • PASSWORD:用户的密码。

并行构建索引

为了更快地构建索引,AlloyDB Omni 可能会自动生成多个并行工作器,具体取决于您的数据集以及您选择的索引类型。当您创建三级或四级 ScaNN 索引或数据集超过 1 亿行时,系统通常会触发此功能。

虽然 AlloyDB Omni 会自动优化并行工作器的数量,但您可以使用以下 PostgreSQL 查询规划参数来对并行工作器进行调优:

如需避免在创建 ScaNN 索引时出现内存不足问题,请确保 maintenance_work_memshared_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

后续步骤