创建和管理向量索引

本页介绍了如何创建和管理 Spanner 向量索引,这些索引使用近似最近邻 (ANN) 搜索和基于树的结构来加快对数据的向量相似度搜索速度。

Spanner 使用专用向量索引来加快近似最近邻 (ANN) 向量搜索的速度。此索引利用了 Google Research 的可扩容最近邻 (ScaNN),这是一种高效的最近邻算法。

向量索引使用基于树的结构来划分数据,从而加快搜索速度。Spanner 提供两级树配置和三级树配置:

  • 二级树配置:叶节点 (num_leaves) 包含密切相关的向量组以及相应的形心。根级由所有叶节点的形心组成。
  • 三级树配置:在概念上与二级树类似,但引入了一个额外的分支层 (num_branches),叶节点形心会从该分支层进一步分区,以形成根级别 (num_leaves)。

Spanner 会为您选择一个索引。不过,如果您知道某个特定索引效果最佳,则可以使用 FORCE_INDEX 提示来选择使用最适合您的用例的向量索引。

如需了解详情,请参阅 VECTOR INDEX 语句

限制

  • 您无法预先拆分向量索引。如需了解详情,请参阅预拆分概览

创建向量索引

为优化向量索引的召回率和性能,我们建议您:

  • 在将大部分包含嵌入的行写入数据库后,创建向量索引。插入新数据后,您可能还需要定期重建向量索引。如需了解详情,请参阅重建向量索引

  • 使用 STORING 子句在向量索引中存储列的副本。如果列值存储在向量索引中,则 Spanner 会在索引的叶级执行过滤,以提升查询性能。如果列用于过滤条件,建议您存储该列。如需详细了解如何在索引中使用 STORING,请参阅为纯索引扫描创建索引

创建表时,嵌入列必须是 FLOAT32(推荐)或 FLOAT64 数据类型的数组,并且具有 vector_length 注解,用于指明向量的维度。

以下 DDL 语句会创建一个 Documents 表,其中包含一个向量长度为 DocEmbedding 的嵌入列:

CREATE TABLE Documents (
  UserId INT64 NOT NULL,
  DocId INT64 NOT NULL,
  Author STRING (1024),
  DocContents Bytes(MAX),
  DocEmbedding ARRAY<FLOAT32>(vector_length=>128) NOT NULL,
  NullableDocEmbedding ARRAY<FLOAT32>(vector_length=>128),
  WordCount INT64,
) PRIMARY KEY (DocId);

填充 Documents 表后,您可以使用余弦距离在 Documents 表上创建一个具有嵌入列 DocEmbedding 的双层树和 1000 个叶节点的向量索引:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(DocEmbedding)
  STORING (WordCount)
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

如果您的嵌入列未在表定义中标记为 NOT NULL,您必须在向量索引定义中使用 WHERE COLUMN_NAME IS NOT NULL 子句声明该列,其中 COLUMN_NAME 是您的嵌入列的名称。如需使用余弦距离在可为 null 的嵌入列 NullableDocEmbedding 上创建具有三级树和 1000000 个叶节点的向量索引,请执行以下操作:

CREATE VECTOR INDEX DocEmbeddingThreeLevelIndex
  ON Documents(NullableDocEmbedding)
  STORING (WordCount)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);

过滤向量索引

您还可以创建过滤后的向量索引,以查找数据库中与过滤条件最匹配的相似项。过滤后的向量索引会选择性地为满足指定过滤条件的行编制索引,从而提高搜索性能。

在以下示例中,表 Documents2 具有一个名为 Category 的列。在矢量搜索中,我们希望为“技术”类别编制索引,因此我们创建了一个生成的列,如果未满足类别条件,则该列的计算结果为 NULL

CREATE TABLE Documents2 (
  DocId INT64 NOT NULL,
  Category STRING(MAX),
  NullIfFiltered BOOL AS (IF(Category = 'Tech', TRUE, NULL)) HIDDEN,
  DocEmbedding ARRAY<FLOAT32>(vector_length=>128),
) PRIMARY KEY (DocId);

然后,我们创建一个带有过滤条件的向量索引。TechDocEmbeddingIndex 向量索引仅对“技术”类别中的文档编制索引。

CREATE VECTOR INDEX TechDocEmbeddingIndex
  ON Documents2(DocEmbedding)
  STORING(NullIfFiltered)
  WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
  OPTIONS (...);

当 Spanner 运行以下查询时,如果查询的过滤条件与 TechDocEmbeddingIndex 匹配,系统会自动选择 TechDocEmbeddingIndex 并通过它来加速查询。该查询仅搜索“技术”类别中的文档。您还可以使用 {@FORCE_INDEX=TechDocEmbeddingIndex} 强制 Spanner 明确使用 TechDocEmbeddingIndex

SELECT *
FROM Documents2
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
ORDER BY APPROX_(....)
LIMIT 10;

后续步骤