创建 ScaNN 索引

本页面介绍了如何使用 ScaNN 索引通过存储的嵌入生成索引和查询嵌入。如需详细了解如何存储嵌入,请参阅存储向量嵌入

AlloyDB alloydb_scann 是由 Google 开发的 PostgreSQL 扩展程序,可实现依托 ScaNN 算法的高效最近邻索引。

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

准备工作

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

  • 嵌入向量已添加到 AlloyDB 数据库中的表

  • 安装了基于 pgvectorvector 扩展程序(由 Google 针对 AlloyDB 进行扩展)和 alloydb_scann 扩展程序:

    CREATE EXTENSION IF NOT EXISTS alloydb_scann CASCADE;
    
  • 如果您想创建自动调优的 ScaNN 索引,请确保已启用 scann.enable_preview_features 标志。如果您不想启用预览版功能,或者要为生产实例创建 ScaNN 索引,可以使用特定参数创建 ScaNN 索引

创建自动调优的 ScaNN 索引

借助自动索引功能,您可以简化索引创建流程,自动创建针对搜索性能优化的索引,或在索引构建时间和搜索性能之间取得平衡。

使用 AUTO 模式时,您只需指定表名称和嵌入列,以及要使用的距离函数。您可以优化索引以提高搜索性能,也可以在索引构建时间和搜索性能之间取得平衡。

您还可以选择使用 MANUAL 模式创建索引,以便精细控制其他索引调整参数。

在 AUTO 模式下创建 ScaNN 索引

AUTO 模式下创建索引之前,请注意以下几点:

  • AlloyDB 无法为数据不足的表创建 ScaNN 索引。
  • AUTO 模式下创建索引时,您无法设置索引创建参数,例如 num_leaves
  • 默认情况下,以 AUTO 模式创建的所有索引都启用了自动维护

如需在 AUTO 模式下创建索引,请先启用功能标志 scann.zero_knob_index_creation。启用该标志后,运行以下命令:

      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:创建可平衡索引构建时间和搜索性能的索引。

在 MANUAL 模式下创建 ScaNN 索引

如果您已启用 scann.enable_preview_features 标志,并且希望精细控制调优参数,则可以在 MANUAL 模式下创建索引。

如需在 MANUAL 模式下创建 ScaNN 索引,请运行以下命令:

      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=MAX_NUM_LEVELS]);

替换以下内容:

  • INDEX_NAME:要创建的索引的名称,例如 my-scann-index。索引名称会在整个数据库中共享。验证每个索引名称对数据库中的每个表都是唯一的。

  • TABLE:要向其中添加索引的表。

  • EMBEDDING_COLUMN:用于存储 vector 数据的列。

  • DISTANCE_FUNCTION:要与此索引一起使用的距离函数。请按以下方式之一操作:

    • L2 距离:l2

    • 点积:dot_product

    • 余弦距离:cosine

  • NUM_LEAVES_VALUE:要应用于此索引的分区数量。设置为介于 1 到 1048576 之间的任意值。

  • QUANTIZER:要使用的量化器类型。可用的选项如下所示:

    • SQ8:可在召回率损失最小(通常低于 1-2%)的情况下提供平衡的查询性能。这是默认值。
    • AH:当列式引擎处于启用状态,并且您的索引和表数据已填充到列式引擎中(受其配置大小的限制)时,请考虑使用此选项,以获得可能更好的查询性能。请注意,与 SQ8 相比,AH 的压缩率最高可达 4 倍。如需了解详情,请参阅调整 ScaNN 的最佳实践
    • FLAT:以牺牲搜索性能为代价,提供 99% 或更高的最高召回率。
  • MAX_NUM_LEVELS:K-means 聚类树的级别数量上限。对于基于二级树的量化,设置为 1(默认值);对于基于三级树的量化,设置为 2

您可以添加其他索引创建或查询运行时参数来调整索引。如需了解详情,请参阅ScaNN 索引进行调优

更改现有索引的模式

如果您使用 AUTO 模式创建了 ScaNN 索引,并且想要手动调整该索引,则必须将模式更改为 MANUAL

如需将模式更改为 MANUAL,请按以下步骤操作:

  1. 更新索引以将模式设置为 MANUAL

    ALTER INDEX INDEX_NAME SET (mode = 'MANUAL', num_leaves = NUM_LEAVES_VALUE);
    

    替换以下内容:

    • INDEX_NAME:要创建的索引的名称,例如 my-scann-index。索引名称会在整个数据库中共享。验证每个索引名称对数据库中的每个表都是唯一的。

    • NUM_LEAVES_VALUE:要应用于此索引的分区数量。设置为介于 1 到 1048576 之间的任意值。

    您可以添加其他索引创建或查询运行时参数来调整索引。如需了解详情,请参阅ScaNN 索引进行调优

  2. 重新构建索引以应用参数:

    REINDEX INDEX CONCURRENTLY INDEX_NAME;
    

如需将模式更改为 AUTO,请完成以下步骤:

  1. 更新索引以将模式设置为 AUTO

    ALTER INDEX INDEX_NAME SET (mode = 'AUTO');
    
  2. 重新构建索引以应用参数:

    REINDEX INDEX CONCURRENTLY INDEX_NAME;
    

创建具有特定参数的 ScaNN 索引

如果您的应用对召回率和索引构建时间有具体要求,则可以手动创建索引。您可以根据工作负载创建二级或三级树索引。如需详细了解如何调整参数,请参阅调整 ScaNN 索引

二级树索引

如需将使用 ScaNN 算法的二级树索引应用于包含存储的向量嵌入的列,请运行以下 DDL 查询:

CREATE INDEX INDEX_NAME ON TABLE
  USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
  WITH (num_leaves=NUM_LEAVES_VALUE, quantizer =QUANTIZER);

替换以下内容:

  • INDEX_NAME:要创建的索引的名称,例如 my-scann-index。索引名称会在整个数据库中共享。确保每个索引名称对数据库中的每个表中都是唯一的。

  • TABLE:要向其中添加索引的表。

  • EMBEDDING_COLUMN:用于存储 vector 数据的列。

  • DISTANCE_FUNCTION:要与此索引一起使用的距离函数。请按以下方式之一操作:

    • L2 距离:l2

    • 点积:dot_product

    • 余弦距离:cosine

  • NUM_LEAVES_VALUE:要应用于此索引的分区数量。设置为介于 1 到 1048576 之间的任意值。如需详细了解如何确定此值,请参阅ScaNN 索引进行调优

  • QUANTIZER:要使用的量化器类型。可用的选项如下所示:

    • SQ8:可在召回率损失最小(通常低于 1-2%)的情况下提供平衡的查询性能。这是默认值。
    • AH:当列式引擎处于启用状态,并且您的索引和表数据已填充到列式引擎中(受其配置大小的限制)时,请考虑使用此选项,以获得可能更好的查询性能。请注意,与 SQ8 相比,AH 的压缩率最高可达 4 倍。如需了解详情,请参阅调整 ScaNN 的最佳实践
    • FLAT:以牺牲搜索性能为代价,提供 99% 或更高的最高召回率。

三级树索引

如需对包含存储的向量嵌入的列创建使用 ScaNN 算法的三级树索引,请运行以下 DDL 查询:

CREATE INDEX INDEX_NAME ON TABLE
  USING scann (EMBEDDING_COLUMN DISTANCE_FUNCTION)
  WITH (num_leaves=NUM_LEAVES_VALUE, max_num_levels = 2);

创建索引后,您可以按照使用给定文本执行最近邻查询中的说明,运行利用该索引的最近邻搜索查询。

索引参数必须设置为可在 QPS 和召回率之间取得适当的平衡。如需详细了解如何对 ScaNN 索引进行调优,请参阅ScaNN 索引进行调优

如需在使用 real[] 数据类型(而非 vector)的嵌入列上创建此索引,请将该列转换为 vector 数据类型:

CREATE INDEX INDEX_NAME ON TABLE
  USING scann (CAST(EMBEDDING_COLUMN AS vector(DIMENSIONS)) DISTANCE_FUNCTION)
  WITH (num_leaves=NUM_LEAVES_VALUE, max_num_levels = MAX_NUM_LEVELS);

DIMENSIONS 替换为嵌入列的维度宽度。如需详细了解如何查找维度,请参阅向量函数中的 vector_dims 函数。

如需实现一致的搜索体验,请在创建 ScaNN 索引时启用自动维护功能。如需了解详情,请参阅维护向量索引。此功能为预览版

如需查看索引编制进度,请使用 pg_stat_progress_create_index 视图:

SELECT * FROM pg_stat_progress_create_index;

phase 列会显示索引创建的当前状态。索引构建阶段完成后,索引对应的行不可见。

如需对索引进行调优以实现平均召回率和 QPS 平衡,请参阅ScaNN 索引进行调优

并行构建索引

为了更快地构建索引,AlloyDB 可能会自动生成多个并行工作器,具体取决于您的数据集以及您选择的索引类型。

如果您要创建 3 级 ScaNN 索引或数据集超过 1 亿行,系统通常会触发并行索引构建。

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

运行查询

在将嵌入存储到数据库中并为其编制索引后,您可以开始查询数据。您无法使用 alloydb_scann 扩展程序运行批量搜索查询。

如需查找嵌入向量的语义最近邻,您可以运行以下示例查询,其中设置了在创建索引期间使用的相同距离函数。

  SELECT * FROM TABLE
    ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY ['EMBEDDING']
    LIMIT ROW_COUNT

替换以下内容:

  • TABLE:包含要与文本进行比较的嵌入的表。

  • INDEX_NAME:要使用的索引的名称,例如 my-scann-index

  • EMBEDDING_COLUMN:包含存储的嵌入的列。

  • DISTANCE_FUNCTION_QUERY:要用于此查询的距离函数。根据创建索引时使用的距离函数,选择以下各项之一:

    • L2 距离:<->

    • 内积:<#>

    • 余弦距离:<=>

  • EMBEDDING:您要为其查找存储的语义最近邻的嵌入向量。

  • ROW_COUNT:要返回的行数。

    如果您只想获得单个最佳匹配项,请指定 1

您还可以使用 embedding() 函数将文本转换为向量。由于 embedding() 会返回 real 数组,因此您必须先将 embedding() 调用明确转换为 vector,然后再将其应用于某个最近邻运算符(例如 <-> 表示 L2 距离)。然后,这些运算符可以使用 ScaNN 索引来查找具有语义最相似的嵌入的数据库行。

后续步骤