ScaNN 索引是 Google 打造的基于树的量化索引,用于近似最近邻搜索。与 HNSW 相比,它可以缩短索引构建时间并减少内存占用。此外,与 HNSW 相比,它可根据工作负载提供更快的 QPS。
准备工作
在开始创建索引之前,您必须完成以下前提条件。
-
如果您尝试对空表或分区表生成 ScaNN 索引,可能会遇到一些问题。如需详细了解生成的错误,请参阅排查 ScaNN 索引错误。 如需在空表或小型表上创建索引,请参阅延迟为空表或几乎为空的表创建索引。
已安装
vector和alloydb_scann扩展程序:CREATE EXTENSION IF NOT EXISTS alloydb_scann CASCADE;安装
alloydb_scann扩展程序会自动检查是否已安装vector扩展程序,如果未安装,则会自动安装。您无需单独手动安装vector。如果您想创建四级 ScaNN 索引,必须先为 AlloyDB Omni 实例启用预览版功能。如需启用预览版功能,请选择以下两种方法之一:
启用
scann.enable_preview_features数据库标志。如需详细了解如何配置数据库标志,请参阅配置数据库标志。
在会话或实例级层将
scann.max_allowed_num_levels数据库标志设置为3。如需在会话级设置标志,请运行以下命令:SET scann.max_allowed_num_levels = 3;如需在实例级设置标志,请使用
--database-flags字段运行gcloud alloydb alloydb instances update。
创建自动调整的索引
自动调优的 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
- L2 距离:
-
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
- L2 距离:
-
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
- L2 距离:
-
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可创建四级索引。
将手动调优的索引转换为自动调优的索引
如需将手动调优的索引转换为自动调优的索引,请完成以下步骤:
重置为手动调整的索引定义的所有查询参数。
ALTER INDEX INDEX_NAME RESET (PARAMETER_NAME);执行以下变量替换操作:
INDEX_NAME:您要转换的索引的名称。例如my_scann_index。索引名称会在整个数据库中共享。确保每个索引名称对数据库中的每个表都是唯一的。PARAMETER_NAME:逗号分隔列表,其中包含您要重置的查询参数的名称。例如num_leaves, quantization。请注意,您必须先重置所有其他查询参数,然后才能重置
num_leaves。
重新编制手动调整的索引,以将其转换为自动调整的索引。
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 索引而不启用延迟索引创建。
启用延迟索引创建
如需启用延迟索引创建,请按以下步骤操作:
确保已启用
scann.enable_index_maintenance标志和以下标志之一:scann.enable_preview_features:此标志还会启用其他预览版功能。scann.enable_index_with_insufficient_data:此标志明确仅启用延迟索引创建功能。
如需配置其使用情况,请使用
gcloudCLI:如需启用延迟索引创建功能以及其他预览版功能,请执行以下操作:
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。
创建 ScaNN 索引。如果您在手动模式下创建索引,请确保
auto_maintenance参数设置为on。如需了解详情,请参阅创建手动调优的索引。
限制
- 自动创建索引的后台进程使用数据库级标志值。即使您使用
SET LOCAL命令设置了任何会话级标志,该进程也会考虑在数据库级设置的标志值。 - 如果您计划在单个事务中将大量数据批量插入到空表中,建议您先运行单个插入事务,然后再创建 ScaNN 索引。
在空表或小型表上强制创建索引
AlloyDB Omni 使用验证功能来防止在空表或行数很少的表上创建 ScaNN 索引,原因如下:
ScaNN 索引的训练数据不足。这可能会导致向量相似度搜索的召回率较低。
写入数据库的性能可能会下降。
我们建议您在性能欠佳时延迟创建索引。
不过,在某些开发或测试场景中,您可能需要在空表或小型表上创建索引。在这些情况下,您可以强制创建索引。请注意,强制创建索引需要 SUPERUSER 权限。
如需强制创建索引,请完成以下步骤:
在数据库中将
scann.allow_blocked_operations会话级参数设置为true:SET scann.allow_blocked_operations = true;如果您用来运行这些查询的用户没有
SUPERUSER权限,请为其分配该权限:CREATE USER USERNAME WITH SUPERUSER PASSWORD PASSWORD;执行以下变量替换操作:
USERNAME:您要向其授予SUPERUSER权限的用户的名称。PASSWORD:用户的密码。
并行构建索引
为了更快地构建索引,AlloyDB Omni 可能会自动生成多个并行工作器,具体取决于您的数据集以及您选择的索引类型。当您创建三级或四级 ScaNN 索引或数据集超过 1 亿行时,系统通常会触发此功能。
虽然 AlloyDB Omni 会自动优化并行工作器的数量,但您可以使用以下 PostgreSQL 查询规划参数来对并行工作器进行调优:
如需避免在创建 ScaNN 索引时出现内存不足问题,请确保 maintenance_work_mem 和 shared_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。