给 pgvector 加 HNSW 索引后,查询变快并不代表验收通过。HNSW 是近似最近邻检索,需要同时确认查询是否用了预期索引,以及候选结果相对精确检索丢失了多少。先保存精确 top-k,再用相同数据、向量和度量对照,可以避免只凭一次响应时间决定上线。
下面在一个数据库会话中创建临时演示表,使用人工随机三维向量演示操作。它不代表真实语义数据或性能测试;本文未在读者数据库运行这些 SQL,不提供加速倍数和召回率承诺。接口依据为 2026 年 10 月 1 日读取的官方项目说明。

验收前固定这五项
- 同一份向量数据快照,过程中不插入、更新或删除。
- 同一查询向量、同一距离度量及相同 top-k。
- 相同过滤条件;有租户、版本约束时不能拿无过滤基线与有过滤查询比较。
- 扩展版本、PostgreSQL 版本和索引参数。
- 代表真实使用的问题集,以及能接受的遗漏和延迟范围。
pgvector 官方项目说明指出,默认精确最近邻搜索具有完整召回;HNSW、IVFFlat 等近似索引以一部分召回换取速度。这里的“完整召回”指相对于同一向量度量的最近邻结果,不能等同于一定找到了能回答问题的正确原文。
在建索引之前保存精确结果
使用独立开发库,先确认管理员已安装 pgvector 扩展,并且当前账号可以启用它。下面所有语句必须在同一会话中执行,临时表会在会话结束后消失,不连接业务表。CREATE EXTENSION 需要相应权限;没有权限时请管理员配置,不跳过错误继续执行。
CREATE EXTENSION IF NOT EXISTS vector;
SELECT extversion FROM pg_extension WHERE extname = 'vector';
CREATE TEMP TABLE hnsw_acceptance_demo (
id integer PRIMARY KEY,
embedding vector(3) NOT NULL
);
SELECT setseed(0.42);
INSERT INTO hnsw_acceptance_demo (id, embedding)
SELECT g, ARRAY[random(), random(), random()]::vector
FROM generate_series(1, 20000) AS g;
ANALYZE hnsw_acceptance_demo;
CREATE TEMP TABLE exact_top10 AS
SELECT id, embedding <=> '[0.2,0.7,0.3]'::vector AS distance
FROM hnsw_acceptance_demo
ORDER BY embedding <=> '[0.2,0.7,0.3]'::vector
LIMIT 10;
SELECT * FROM exact_top10 ORDER BY distance, id;
临时表目前只有主键索引,没有向量近似索引,所以 distance 排序形成精确基线。这里用余弦距离 <=>,结果越小越近。不要混成 L2 查询后拿同一份 exact_top10 比较。
建立匹配度量的索引并查看查询计划
CREATE INDEX demo_hnsw_cosine_idx
ON hnsw_acceptance_demo USING hnsw (embedding vector_cosine_ops);
ANALYZE hnsw_acceptance_demo;
SET hnsw.ef_search = 40;
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, embedding <=> '[0.2,0.7,0.3]'::vector AS distance
FROM hnsw_acceptance_demo
ORDER BY embedding <=> '[0.2,0.7,0.3]'::vector
LIMIT 10;
核对计划是否出现使用 demo_hnsw_cosine_idx 的 Index Scan,并保留完整计划与 Execution Time。若是 Seq Scan 加排序,这轮尚未验证 HNSW 路径,不能把“与精确结果一致”当作近似检索召回通过。
数据规模较小或成本估算认为顺序扫描更划算时,规划器可能不选择向量索引。先检查 ORDER BY 是否是距离操作符升序、是否有 LIMIT、操作符与索引的 vector_cosine_ops 是否匹配。仅作诊断时可在这个开发会话中设置 SET enable_seqscan = off,再看计划;诊断后恢复 SET enable_seqscan = on。不要为了让截图出现索引扫描而全局禁用顺序扫描。
计算召回,再调整一次查询候选范围
只有确认相同查询走了 HNSW 后,才记录下面的候选集合。Recall@10 定义为“近似前 10 个 ID 与精确前 10 个 ID 的交集数量 / 10”;同距离并列时需制定一致的并列处理规则。
CREATE TEMP TABLE ann_top10 AS
SELECT id, embedding <=> '[0.2,0.7,0.3]'::vector AS distance
FROM hnsw_acceptance_demo
ORDER BY embedding <=> '[0.2,0.7,0.3]'::vector
LIMIT 10;
SELECT
COUNT(*)::numeric / (SELECT COUNT(*) FROM exact_top10) AS recall_at_10
FROM exact_top10 e
JOIN ann_top10 a USING (id);
SET hnsw.ef_search = 100;
EXPLAIN (ANALYZE, BUFFERS)
SELECT id
FROM hnsw_acceptance_demo
ORDER BY embedding <=> '[0.2,0.7,0.3]'::vector
LIMIT 10;
TRUNCATE ann_top10;
INSERT INTO ann_top10
SELECT id, embedding <=> '[0.2,0.7,0.3]'::vector AS distance
FROM hnsw_acceptance_demo
ORDER BY embedding <=> '[0.2,0.7,0.3]'::vector
LIMIT 10;
SELECT
COUNT(*)::numeric / (SELECT COUNT(*) FROM exact_top10) AS recall_at_10
FROM exact_top10 e
JOIN ann_top10 a USING (id);
官方说明中 hnsw.ef_search 控制查询的动态候选列表,默认值为 40;提高通常可改善召回,代价是更多搜索工作。100 是本演示的对照值,不是所有知识库的推荐配置。每次调整后再次看计划,避免因实际执行路径不同得出错误结论。
把单条演示扩展成真实验收
对自己的固定问题集重复以上流程,记录 query_id、精确 ID、近似 ID、Recall@k、返回数、查询计划和耗时。语义层还需人工标注预期原文:精确 top-k 也可能语义不相关,不能只优化向量邻居一致性。
至少比较两种情况:普通检索与包含实际租户、版本条件的检索。官方项目说明提醒,近似索引中的过滤会影响可返回结果,过滤选择性较高时可能不足 k 条;可在自己扩展版本支持的范围内评估 iterative scan 等策略。先保存实际结果和计划,再决定调候选范围还是改索引布局,不能通过取消访问条件解决漏召回。
性能测试应重复多次,区分预热与冷缓存,并使用预期并发和数据规模。EXPLAIN ANALYZE 会真正执行查询且有测量开销,单次 Execution Time 只是诊断数据,不作为线上延迟承诺。
通过与返工怎么判断
| 观察结果 | 处理 |
|---|---|
| 没走 HNSW 索引 | 先修查询、索引度量或核对规划器选择,暂不签收 ANN 召回 |
| 召回低于自己预先设定的要求 | 调整 ef_search 或索引方案,用同一基线再测 |
| 召回符合要求但延迟不符合要求 | 检查资源、并发和索引参数,不能以召回抵消性能缺口 |
| 向量邻居一致,但原文答不了问题 | 转到嵌入、分段和资料质量验证,索引层不背书答案质量 |
| 过滤后出现越权资料 | 访问边界失败,停止该方案进入发布使用 |
下一步是先为自己的数据保留精确基线与实际查询计划,再填入对照结果。没有这两项,就无法判断 HNSW 的收益、遗漏和边界。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30288.html