pgvector 社区发布了备受期待的 0.8.0 版本,该版本在性能和功能方面均有显著改进。我们当然迫不及待地想将它提供给 Nile 用户。
发布亮点
根据官方发布说明,pgvector 0.8.0 包括:
- 增加了对迭代索引扫描的支持
- 为 sparsevec 添加了数组强制类型转换
- 改进成本估算,以便在过滤时更好地选择索引
- 改进了 HNSW 索引扫描的性能
- 改进了 HNSW 插入和磁盘索引构建的性能
- 不再支持 Postgres 12
最令人期待的功能是迭代索引扫描,它解决了向量索引长期以来的挑战。以前,过滤器是在索引扫描完成后应用的,这通常会产生比预期更少的结果。根据 pgvector 文档:
对于近似索引,在扫描索引后应用过滤。如果条件匹配 10% 的行,则使用 HNSW 和默认的 hnsw.ef_search 40,平均只有 4
行匹配。
常见的解决方法包括扫描更多行、使用部分索引或分区,但这些方法可能不切实际或不受欢迎。新的迭代扫描功能提供了更直接、更直观的解决方案:
- 扫描向量索引
- 应用过滤器
- 检查是否返回了足够的结果。如果没有,请重复扫描。
让我们用一个很小的例子来看一下它的实际效果。我强烈建议进行一些小实验——即使实际结果不符合你的预期,你也能学到很多东西。请继续学习重要的 pgvector 课程:
首先,我创建一个包含示例数据的表:
CREATE TABLE filtest(id INTEGER, category INTEGER, embedding vector(3));
INSERT INTO filtest VALUES
(1, 1, '[3, 1, -2]'),
(2, 1, '[3, 1, -2]'),
(3, 1, '[3, 1, -2]'),
(1, 2, '[1.1, 2.2, 3.3]'),
(2, 2, '[1.1, 2.2, 3.3]'),
(3, 2, '[1.1, 2.2, 3.3]');
CREATE INDEX ON filtest USING hnsw (embedding vector_cosine_ops);
该表包含六行,分为两类。第 2 类中的向量非常相似[1, 2, 3],而第 1 类中的向量则有很大差异,表示正交(不相关)数据。在实际用例中,类别可以是不同的公司(租户)、同一公司内的不同部门、类型(如果表存储有关电影的信息)等。您可能想要用于过滤的任何内容。
您希望以下查询返回什么?
SELECT id, category, embedding<=>'[1,2,3]' AS distance
FROM filtest WHERE category=1
ORDER BY distance LIMIT 3;
pgvector 0.7.4 中的索引扫描
我从类别 1 中搜索了最接近的 3 个向量[1,2,3],因此正确的答案是返回类别 1 中的所有向量。但是,这是我期望 pgvector 0.7.4 执行的操作:
- 扫描向量索引并找到最接近的3个向量[1,2,3],其中一些应该属于类别2。
- 过滤结果并仅保留类别 1 的向量。
- 返回部分结果。
然而,在实践中:
SELECT extversion FROM pg_extension WHERE extname = 'vector';
extversion
------------
0.7.4
(1 row)
SELECT id, category, embedding<=>'[1,2,3]' AS distance
FROM filtest WHERE category=1 ORDER BY distance LIMIT 3;
id | category | distance
----+----------+--------------------
3 | 1 | 1.0714285714285714
1 | 1 | 1.0714285714285714
2 | 1 | 1.0714285714285714
为什么我们看到正确的结果?因为 pgvector 默认配置的向量索引中要扫描的行数为 40。因此索引扫描返回了整个表,经过筛选后,我们得到了正确的结果。这就是小示例的问题所在……如果我尝试使用 100 个向量,它会像预期的那样错过一些结果。
让我们尝试调整配置:
SET hnsw.ef_search = 3;
SET
SELECT id, category, embedding<=>'[1,2,3]' AS distance
FROM filtest WHERE category=1 ORDER BY distance LIMIT 3;
id | category | distance
----+----------+--------------------
3 | 1 | 1.0714285714285714
2 | 1 | 1.0714285714285714
(2 rows)
这是我所期望的部分结果!
pgvector 0.8.0 中的迭代索引扫描
现在让我们看看相同的查询在 pgvector 0.8.0 中的行为:
SELECT extversion FROM pg_extension WHERE extname = 'vector';
extversion
------------
0.8.0
SET hnsw.ef_search = 3;
SET
SELECT id, category, embedding<=>'[1,2,3]' AS distance
FROM filtest WHERE category=1 ORDER BY distance LIMIT 3;
id | category | distance
----+----------+--------------------
1 | 1 | 1.0714285714285714
2 | 1 | 1.0714285714285714
3 | 1 | 1.0714285714285714
(3 rows)
太棒了!pgvector 0.8.0 交付了!是的,但不是你想的那样:
SHOW hnsw.iterative_scan;
hnsw.iterative_scan
---------------------
off
(1 row)
哎呀!hnsw.iterative_scan已禁用。那么它怎么还能工作呢?使用explain显示未使用索引:
EXPLAIN SELECT id, category, embedding<=>'[1,2,3]' AS distance
FROM filtest WHERE category=1 ORDER BY distance LIMIT 3;
QUERY PLAN
---------------------------------------------------------------------
Limit (cost=25.09..25.10 rows=3 width=16)
-> Sort (cost=25.09..25.11 rows=6 width=16)
Sort Key: ((embedding <=> '[1,2,3]'::vector))
-> Seq Scan on filtest (cost=0.00..25.02 rows=6 width=16)
Filter: (category = 1)
(5 rows)
这是由于 pgvector 0.8.0 中的不同改进造成的:
改进成本估算,以便在过滤时更好地选择索引
在扫描 6 行表时使用向量索引确实很愚蠢。因此,随着成本估算的改进,这种情况不再发生。这是个好消息,但我真的想检查迭代扫描。因此,让我们强制使用索引并重试:
SET enable_seqscan=false; -- force use of index, don't do this in production!
SET
SELECT id, category, embedding<=>'[1,2,3]' AS distance
FROM filtest WHERE category=1 ORDER BY distance LIMIT 3;
id | category | distance
----+----------+--------------------
2 | 1 | 1.0714285714285714
3 | 1 | 1.0714285714285714
(2 rows)
SET hnsw.iterative_scan = relaxed_order; -- enable iterative scan
SET
SELECT id, category, embedding<=>'[1,2,3]' AS distance
FROM filtest WHERE category=1 ORDER BY distance LIMIT 3;
id | category | distance
----+----------+--------------------
2 | 1 | 1.0714285714285714
3 | 1 | 1.0714285714285714
1 | 1 | 1.0714285714285714
(3 rows)
我们可以看到 pgvector 0.8.0 和迭代扫描按预期工作。作为额外收获,我们还看到了成本优化的实际应用,并了解了hnsw.ef_search查询配置。最后一条提示:在生产使用中,您可能需要在用于过滤的列上建立索引,而不仅仅是向量索引。
如果您是 Nile 用户,您已经拥有 pgvector 0.8.0,因此我建议您充分利用它。请前往我们的文档, 详细了解 iterative_scan 选项和花哨的优化。
#PG证书#PG考试#postgresql培训#postgresql考试#postgresql认证
原文链接:https://www.thenile.dev/blog/pgvector-080
作者:Gwen Shapira