本页介绍了如何在 Spanner 中使用以下 ANN 距离函数查找近似最近邻 (ANN)、创建向量索引以及查询向量嵌入:
APPROX_COSINE_DISTANCE
APPROX_EUCLIDEAN_DISTANCE
APPROX_DOT_PRODUCT
如果数据集较小,您可以使用K 最近邻 (KNN) 查找精确的 k 个最近邻向量。不过,随着数据集的增长,KNN 搜索的延迟时间和费用也会增加。您可以使用 ANN 找到近似的 k 个最近邻,从而显著缩短延迟时间并降低费用。
近似 K 近邻
在 ANN 搜索中,返回的 k 个向量不是真正的前 k 个最近邻,因为 ANN 搜索会计算近似距离,并且可能不会查看数据集中的所有向量。有时,系统会返回一些不在前 k 个最近邻中的向量。这称为“召回率损失”。您可以接受的召回率损失程度取决于具体用例,但在大多数情况下,为了提高数据库性能而牺牲一点召回率是一个可以接受的权衡。
如需详细了解 Spanner 近似距离函数,请参阅:
- GoogleSQL 中的
APPROX_COSINE_DISTANCE
- GoogleSQL 中的
APPROX_EUCLIDEAN_DISTANCE
- GoogleSQL 中的
APPROX_DOT_PRODUCT
向量索引
Spanner 使用专用向量索引加快 ANN 向量搜索速度。此索引利用 Google 研究团队的可伸缩最近邻 (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 {
...
DocEmbedding ARRAY<FLOAT32>(vector_length=>128);
};
填充 Documents
表后,您可以使用余弦距离在具有嵌入列 DocEmbedding
的 Documents
表上创建一个包含两级树和 1, 000 个叶节点的向量索引:
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(DocEmbedding)
STORING (WordCount)
OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);
如需创建一个三层树和 1000000 个叶节点的向量索引,请执行以下操作:
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(NullableDocEmbedding)
STORING (WordCount)
WHERE NullableDocEmbedding IS NOT NULL
OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);
如果您的嵌入列未在表定义中标记为 NOT NULL,则必须使用 WHERE column_name IS NOT NULL
子句声明该列:
CREATE VECTOR INDEX DocEmbeddingIndex
ON Documents(NullableDocEmbedding)
STORING (WordCount)
WHERE NullableDocEmbedding IS NOT NULL
OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);
查询向量嵌入
如需查询矢量索引,请使用以下三个近似距离函数之一:
APPROX_COSINE_DISTANCE
APPROX_EUCLIDEAN_DISTANCE
APPROX_DOT_PRODUCT
使用近似距离函数时,存在以下限制:
- 您必须使用常量表达式作为距离函数的一个参数(例如参数或字面量)。
- 使用近似距离函数的查询或子查询必须采用特定形式:距离函数必须是唯一的
ORDER BY
键,并且必须指定上限。
如需查看限制的详细列表,请参阅近似距离函数参考页面。
示例
如需搜索与 [1.0, 2.0, 3.0]
距离最近的 100 个向量,请执行以下操作:
SELECT DocId
FROM Documents
WHERE WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
ARRAY<FLOAT32>[1.0, 2.0, 3.0], DocEmbedding,
options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100
如果嵌入列可以为 null:
SELECT DocId
FROM Documents
WHERE NullableDocEmbedding IS NOT NULL AND WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
ARRAY<FLOAT32>[1.0, 2.0, 3.0], NullableDocEmbedding,
options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100
最佳做法
请遵循以下最佳实践,优化向量索引并改进查询结果。
调整向量搜索选项
最优的矢量搜索值取决于使用情形、矢量数据集和查询矢量。您可能需要进行迭代调整,以找到适合您的特定工作负载的最佳值。
在选择合适的值时,请遵循以下实用准则:
tree_depth
(树级):如果要编制索引的表的行数少于 1, 000 万行,请将tree_depth
设为2
。否则,3
的tree_depth
支持的表格最多可包含约 100 亿行。num_leaves
:使用数据集中行数的平方根。值越大,向量索引构建时间就越长。避免将num_leaves
设置为大于table_row_count/1000
,因为这会导致叶片过小且性能不佳。num_leaves_to_search
:此选项用于指定要搜索的索引叶节点的数量。提高num_leaves_to_search
会提高召回率,但也会增加延迟时间和费用。我们建议将CREATE VECTOR INDEX
语句中定义的叶子总数的 1% 作为num_leaves_to_search
的值。如果您使用的是过滤子句,请提高此值以扩大搜索范围。
如果达到了可接受的召回率,但查询费用过高,导致最大 QPS 较低,请尝试按照以下步骤提高 num_leaves
:
- 将
num_leaves
设置为其原始值的 k 倍(例如2 * sqrt(table_row_count)
)。 - 将
num_leaves_to_search
设为与其原始值相同的 k 倍。 - 尝试降低
num_leaves_to_search
,以便在保持召回率的同时降低费用和 QPS。
提高回想度
导致召回率恶化的原因有多种,包括:
num_leaves_to_search
太小:您可能会发现,查找某些查询向量的最近邻更具挑战性,因此增加num_leaves_to_search
以搜索更多叶子可以帮助提高召回率。最近的查询可能包含更多这类具有挑战性的向量。矢量索引需要重建:矢量索引的树结构会在创建时针对数据集进行优化,之后会保持静态。因此,如果在创建初始向量索引后添加了明显不同的向量,则树结构可能不太理想,导致检索率较低。
重建向量索引
如需在不停机的情况下重建向量索引,请执行以下操作:
- 在与当前向量索引相同的嵌入列上创建新的向量索引,并根据需要更新参数(例如
OPTIONS
)。 - 索引创建完成后,使用
FORCE_INDEX
提示指向新索引以更新矢量搜索查询。这样可以确保查询使用新的向量索引。您可能还需要在新查询中重新调整num_leaves_to_search
。 - 删除过时的矢量索引。
后续步骤
详细了解 GoogleSQL
APPROXIMATE_COSINE_DISTANCE()
、APPROXIMATE_EUCLIDEAN_DISTANCE()
、APPROXIMATE_DOT_PRODUCT()
函数。如需查看使用 ANN 的分步示例,请参阅 Spanner Vector Search 使用入门。