对向量查询性能进行调优

选择文档版本:

本文档介绍了如何对索引进行调优以实现更快的查询性能和更高的召回率。

ScaNN 索引进行调优

ScaNN 索引使用基于树量化的索引编制。在树量化技术中,索引会学习搜索树以及量化(或哈希)函数。运行查询时,搜索树用于对搜索空间进行剪枝,而量化用于压缩索引大小。这种剪枝可以加快对查询向量与数据库向量之间的相似度(即距离)进行打分的速度。

如需在使用最近邻查询时实现较高的每秒查询次数速率 (QPS) 和较高的召回率,您必须以最适合您的数据和查询的方式对 ScaNN 索引的树进行分区。

在构建 ScaNN 索引之前,请完成以下操作:

  • 确保已创建包含您数据的表。
  • 确保您为 maintenance_work_memshared_buffers 标志设置的值小于机器总内存,以免在生成索引时出现问题。

调节参数

以下索引参数和数据库标志可结合使用,以便在召回率和 QPS 之间取得适当的平衡。所有参数都适用于这两种 ScaNN 索引类型。

调优参数 说明 参数类型
num_leaves 要应用于此索引的分区数量。创建索引时所应用的分区数量会影响索引性能。通过为一定数量的向量增加分区,您可以创建更精细的索引,从而提高召回率和查询性能。不过,这会以延长索引创建时间为代价。

由于三级树的构建速度比二级树更快,因此您可以在创建三级树索引时增加 num_leaves_value,以实现更好的性能。
  • 二级索引:将此值设置为 11048576 之间的任意值。

    如果您不确定要选择的确切值,请在开始时使用 sqrt(ROWS),其中 ROWS 是向量行数。每个分区包含的向量数量通过
    ROWS/sqrt(ROWS) = sqrt(ROWS) 进行计算。

    由于可以对向量行数少于 1,000 万的数据集创建二级树索引,因此每个分区会包含少于 (sqrt(10M)) 个向量(即 3200 个向量)。为获得最佳性能,建议尽可能减少每个分区中的向量数量。
  • 三级索引:将此值设置为 11048576 之间的任意值。

    如果您不确定要选择的确切值,请在开始时使用 power(ROWS, 2/3),其中 ROWS 是向量行数。每个分区包含的向量数量通过
    ROWS/power(ROWS, 2/3) = power(ROWS, 1/3) 进行计算。

    由于可以对向量行数超过 1 亿的数据集创建三级树索引,因此每个分区会包含超过
    (power(100M, 1/3)) 个向量(即 465 个向量)。为获得最佳性能,建议尽可能减少每个分区中的向量数量。
索引创建
quantizer 您要用于 K-means 树的量化器的类型。默认值为 SQ8,用于提高查询性能。

将其设置为 FLAT 可提高召回率。
索引创建
enable_pca 启用主成分分析 (PCA),这是一种降维技术,用于尽可能自动缩减嵌入的大小。此选项默认处于启用状态。

如果您发现召回率下降,请将其设置为 false
索引创建
scann.num_leaves_to_search 此数据库标志控制召回率和 QPS 之间的权衡。默认值为 num_leaves 中设置的值的 1%。

设置的值越高,召回率越高,但 QPS 越低,反之亦然。
查询运行时
scann.max_top_neighbors_buffer_size 数据库标志用于指定缓存的大小,该缓存通过对内存中扫描到的候选邻居进行评分或排名来提高过滤查询的性能,而不是使用磁盘。默认值为 20000

设置的值越高,过滤查询下的 QPS 越好,但会导致内存使用量更高,反之亦然。
查询运行时
scann.pre_reordering_num_neighbors 在进行设置后,该数据库标志会指定在初始搜索确定一组候选项后,在重新排序阶段要考虑的候选近邻项的数量。请将此参数设置为一个大于您希望查询返回的近邻项数量的值。

值越高,召回率越高,但这种方法会导致 QPS 降低。
查询运行时
max_num_levels K-means 聚类树的级别数量上限。
  • 二级树索引:默认情况下,对于基于二级树的量化,设置为此值。
  • 三级树索引:对于基于三级树的量化,显式设置为 2
索引创建

ScaNN 索引进行调优

请考虑以下关于二级和三级 ScaNN 索引的示例,它们展示了如何设置调优参数:

二级索引

SET LOCAL scann.num_leaves_to_search = 1;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 1/2)]);

三级索引

SET LOCAL scann.num_leaves_to_search = 10;
SET LOCAL scann.pre_reordering_num_neighbors=50;

CREATE INDEX my-scann-index ON my-table
  USING scann (vector_column cosine)
  WITH (num_leaves = [power(1000000, 2/3)], max_num_levels = 2);

对已生成 ScaNN 索引的表执行的任何插入或更新操作都会影响学习型树优化索引的方式。如果您的表容易频繁更新或插入,我们建议您定期重新编制现有的 ScaNN 索引,以提高召回率准确率。

您可以监控索引指标,以确定自构建索引以来创建的突变量,然后相应地重新编制索引。如需详细了解指标,请参阅向量索引指标

调优方面的最佳实践

根据您计划使用的 ScaNN 索引类型,调优索引的建议会有所不同。本部分提供了有关如何对索引参数进行调优以在召回率和 QPS 之间取得最佳平衡的建议。

二级树索引

如需应用建议以帮助您为数据集找到 num_leavesnum_leaves_to_search 的最佳值,请按照以下步骤操作:

  1. 创建 ScaNN 索引,并将 num_leaves 设置为已编入索引的表的行数的平方根。
  2. 运行测试查询,并增加 scann.num_of_leaves_to_search 的值,直到达到目标召回率范围(例如 95%)为止。如需详细了解如何分析查询,请参阅分析查询
  3. 记下 scann.num_leaves_to_searchnum_leaves 之间的比率,此比率将在后续步骤中使用。此比率可提供关于数据集的近似值,有助于您实现目标召回率。

    如果您使用的是高维向量(500 个维度或更高),并且想要提高召回率,请尝试对 scann.pre_reordering_num_neighbors 的值进行调优。首先,将该值设置为 100 * sqrt(K),其中 K 是您在查询中设置的限制。
  4. 如果您的查询达到目标召回率后 QPS 过低,请按照以下步骤操作:
    1. 重新创建索引,并根据以下指导增加 num_leavesscann.num_leaves_to_search 的值:
      • num_leaves 设置为行数平方根的更大倍数。例如,如果索引的 num_leaves 设置为行数的平方根,请尝试将其设置为平方根的两倍。如果该值已是两倍,请尝试将其设置为平方根的三倍。
      • 根据需要增加 scann.num_leaves_to_search,以使其与 num_leaves 保持您在第 3 步中记录的比率。
      • num_leaves 设置为小于或等于行数除以 100 的值。
    2. 再次运行测试查询。在运行测试查询时,尝试减少 scann.num_leaves_to_search,找到一个可以增加 QPS,同时保持较高召回率的值。在不重建索引的情况下尝试使用不同的 scann.num_leaves_to_search 值。
  5. 重复第 4 步,直到 QPS 和召回率范围都达到可接受的值。

三级树索引

除了针对二级树 ScaNN 索引的建议之外,还请遵循以下指导并按照以下步骤对索引进行调优:

  • 将二级树的 max_num_levels1 增加到三级树的 2 可显著缩短创建索引的时间,但会牺牲召回率准确性。使用以下建议设置 max_num_levels
    • 如果向量行数超过 1 亿行,请将该值设置为 2
    • 如果向量行数少于 1,000 万行,请将该值设为 1
    • 如果向量行数介于 1,000 万行和 1 亿行之间,请根据您需要的索引创建时间和召回准确度之间的平衡,将该值设置为 12

如需应用建议以查找 num_leavesmax_num_levels 索引参数的最佳值,请按照以下步骤操作:

  1. 根据您的数据集,使用以下 num_leavesmax_num_levels 组合创建 ScaNN 索引:

    • 向量行数超过 1 亿行:将 max_num_levels 设置为 2,并将 num_leaves 设置为 power(rows, ⅔)
    • 向量行数少于 1 亿行:将 max_num_levels 设置为 1,并将 num_leaves 设置为 sqrt(rows)
    • 向量行数介于 1,000 万行和 1 亿行之间:首先将 max_num_levels 设置为 1,并将 num_leaves 设置为 sqrt(rows)
  2. 运行测试查询。如需详细了解如何分析查询,请参阅分析查询

    如果索引创建时间令人满意,则保留 max_num_levels 值,并尝试使用 num_leaves 值以获得最佳召回准确率。

  3. 如果您对索引创建时间不满意,请执行以下操作:

    • 如果 max_num_levels 值为 1,则舍弃索引。重建索引,并将 max_num_levels 值设置为 2

      运行查询并调整 num_leaves 值,以实现最佳召回准确度。

    • 如果 max_num_levels 值为 2,则舍弃索引。使用相同的 max_num_levels 值重建索引,并调整 num_leaves 值以实现最佳召回准确率。

IVF 索引进行调优

对您为 listsivf.probesquantizer 参数设置的值进行调优可能会有助于优化应用性能:

调优参数 说明 参数类型
lists 在索引构建期间创建的列表数量。为此值设置的初始值:最多 100 万行时为 (rows)/1000,超过 100 万行时为 sqrt(rows) 索引创建
quantizer 您要用于 K-means 树的量化器的类型。默认值为 SQ8,用于提高查询性能。将其设置为 FLAT 可提高召回率。 索引创建
ivf.probes 在搜索期间要探索的最近邻列表的数量。此值的初始值为
sqrt(lists)
查询运行时

请考虑以下示例,其中显示设置了调优参数的 IVF 索引:

SET LOCAL ivf.probes = 10;

CREATE INDEX my-ivf-index ON my-table
  USING ivf (vector_column cosine)
  WITH (lists = 100, quantizer = 'SQ8');

IVFFlat 索引进行调优

对您为 listsivfflat.probes 参数设置的值进行调优可能会有助于优化应用性能:

调优参数 说明 参数类型
lists 在索引构建期间创建的列表数量。为此值设置的初始值:最多 100 万行时为 (rows)/1000,超过 100 万行时为 sqrt(rows) 索引创建
ivfflat.probes 在搜索期间要探索的最近邻列表的数量。此值的初始值为
sqrt(lists)
查询运行时

在构建 IVFFlat 索引之前,请确保为数据库的 max_parallel_maintenance_workers 标志设置的值足以对大型表加快索引创建。

请考虑以下示例,其中显示设置了调优参数的 IVFFlat 索引:

SET LOCAL ivfflat.probes = 10;

CREATE INDEX my-ivfflat-index ON my-table
  USING ivfflat (vector_column cosine)
  WITH (lists = 100);

HNSW 索引进行调优

对您为 mef_constructionhnsw.ef_search 参数设置的值进行调优可能会有助于优化应用性能。

调优参数 说明 参数类型
m 图中每个节点的连接数上限。您可以先设置默认值 16(默认值),然后根据数据集的大小尝试更高的值。 索引创建
ef_construction 在图构建期间维护的动态候选项列表的大小,该列表会不断更新节点最近邻的当前最佳候选项。将此值设置为大于 m 值两倍的任何值,例如 64(默认值)。 索引创建
ef_search 搜索期间使用的动态候选项列表的大小。您可以先将此值设置为 mef_construction,然后在观察召回率期间进行更改。默认值为 40 查询运行时

请考虑以下示例,其中显示设置了调优参数的 hnsw 索引:

SET LOCAL hnsw.ef_search = 40;

CREATE INDEX my-hnsw-index ON my-table
  USING hnsw (vector_column cosine)
  WITH (m = 16, ef_construction = 200);

分析查询

如以下 SQL 查询示例所示,使用 EXPLAIN ANALYZE 命令分析查询分析洞见。

  EXPLAIN ANALYZE SELECT result-column FROM my-table
    ORDER BY EMBEDDING_COLUMN ::vector
    USING INDEX my-scann-index
    <-> embedding('textembedding-gecko@003', 'What is a database?')
    LIMIT 1;

示例响应 QUERY PLAN 包含所用时间、扫描或返回的行数以及使用的资源等信息。

Limit  (cost=0.42..15.27 rows=1 width=32) (actual time=0.106..0.132 rows=1 loops=1)
  ->  Index Scan using my-scann-index on my-table  (cost=0.42..858027.93 rows=100000 width=32) (actual time=0.105..0.129 rows=1 loops=1)
        Order By: (embedding_column <-> embedding('textgecko@003', 'What is a database?')::vector(768))
        Limit value: 1
Planning Time: 0.354 ms
Execution Time: 0.141 ms

查看向量索引指标

您可以使用向量索引指标来查看向量索引的性能、确定需要改进的方面,以及在需要时根据指标对索引进行调优。

如需查看所有向量索引指标,请运行以下 SQL 查询,该查询使用 pg_stat_ann_indexes 视图:

SELECT * FROM pg_stat_ann_indexes;

如需详细了解指标的完整列表,请参阅向量索引指标

后续步骤