Appearance
02. ANN 索引与召回权衡
版本:
v1.1最后更新:
2026-07-08适用对象:已经知道向量数据库是做什么的,但想真正理解
exact vs approximate、HNSW / IVF / Flat / 量化、召回率和延迟为什么总是在互相拉扯的工程同学
很多人第一次做向量检索时,会先问:
- HNSW 是不是最强
- IVF 和 Flat 到底差在哪
- 为什么同样的 embedding,换个索引效果会变
- 为什么写入、删除、内存和查询速度总是在互相打架
这篇不按“算法名词百科”来讲,而是从工程问题出发解释:
- 向量索引到底在替你省什么成本
- 为什么大多数系统不会默认做全量精确搜索
- 什么时候应该优先追求召回率,什么时候先守住延迟和成本
1. 先把问题说清楚:索引不是为了更学术,而是为了不扫全表
给定一个查询向量,最直觉的办法是:
- 对库里每个向量都算一次距离。
- 取最相近的前
k条。
这个办法最容易理解,也最接近“真相”,但规模一大就会遇到现实问题:
- 向量数很大
- 维度很高
- 查询并发上来
- 成本和延迟开始失控
所以索引真正做的事,是把:
每次都精确全量比对
变成:
尽量少看一些候选,但仍然大概率把正确邻居找回来
这就是 ANN 的工程意义。
2. Exact Search 和 ANN 到底差在哪里
2.1 Exact Search 的优点
- 结果最接近真实最近邻
- 便于做基线和离线对照
- 适合小数据集或高精度回归验证
2.2 Exact Search 的代价
- 数据规模一大,查询代价迅速上升
- 高并发时吞吐不划算
- 延迟更难稳定
根据 pgvector 当前官方 README,Postgres 向量检索同时支持:
- exact nearest neighbor search
- approximate nearest neighbor search
这本身就说明:
近似检索不是“功能缩水版”,而是生产规模下的主力模式
2.3 ANN 的核心交换
ANN 不是免费午餐,它做的是典型交换:
- 少量召回损失
- 换更好的查询速度、吞吐和可扩展性
所以你在调索引时,实际上是在调一组业务取舍:
- 召回率
- top-k 命中率
- P95 / P99 延迟
- 内存与存储占用
- 构建时间和更新便利性
3. 向量索引最值得先理解的四类形态
3.1 Flat
Flat 更像:
- 没做近似压缩,直接按原始向量做精确或近似扫描
它的特点通常是:
- 简单
- 基线稳定
- 小集合可用
- 大规模成本高
根据 Weaviate 当前官方文档,flat 是明确可选的索引类型之一。
3.2 HNSW
HNSW 是目前最常见的一类 ANN 思路。
它常被工程团队喜欢,不是因为名字热门,而是因为它在很多实际数据规模下能给出不错的平衡:
- 查询速度快
- 召回率通常较高
- 参数可调空间大
pgvector 当前 README 支持 HNSW 索引,Qdrant 当前官方文档也把搜索建立在 HNSW 思路上,Weaviate 也把 hnsw 作为核心索引选项之一。
3.3 IVF / IVFFlat
IVF 类方法更像先做粗分桶,再在候选桶内继续检索。
它在工程上的意义通常是:
- 用更低的代价先缩小搜索面
pgvector 当前 README 也支持 IVFFlat,这意味着即使在 Postgres 体系里,团队也经常要在:
更像数据库原生集成更像专用 ANN 调优
之间做平衡。
3.4 压缩与量化
当向量规模继续变大时,很多系统会引入:
- 量化
- 压缩
- 更紧凑的索引存储
代价通常是:
- 召回进一步受影响
- 调试更复杂
Milvus 当前官方文档把索引类型和搜索参数分开讲,本质上就在提醒你:
- 索引不只是一个静态建表动作,而是持续调优面
3.5 Cluster / Disk-aware 形态提醒我们:ANN 不只是一张内存图
Weaviate 当前文档除了 flat / hnsw / dynamic,还单独介绍了 HFresh 这类 cluster-based index;Milvus 当前索引家族里也已经不仅有 FLAT / IVF / HNSW,还扩展到 SCANN、HNSW_SQ、HNSW_PQ、IVF_SQ8、IVF_PQ 等不同压缩和检索组合。
这说明一个越来越现实的趋势:
- ANN 不是只在“HNSW 和 IVF 二选一”
而是在向:
- 图索引
- 倒排分桶
- 压缩编码
- 磁盘 / 内存分层
- refinement / rescoring
这些能力组合演进。
4. 不要只问“哪个索引最好”,要问“你在优化哪条链路”
工程里真正会做选择的不是“算法爱好”,而是下面这些问题:
4.1 你是读多写少,还是写入也很频繁
如果知识库变化快、增量更新频繁,就不能只测查询:
- 构建时间
- upsert 成本
- 删除传播
- 重建窗口
都会影响索引选型。
4.2 你是大单库,还是大量中小租户
Weaviate 当前官方文档提到 dynamic index 会从 flat 起步,在规模到阈值后再切向 HNSW。
这个能力特别适合:
- 租户规模差异很大
- 小租户没必要一上来就用重索引
- 大租户又必须追求更高性能
4.3 你是更怕错过,还是更怕慢
不同业务对召回损失的容忍度不一样:
- 客服问答更怕错过关键制度
- 推荐候选池可能更能接受近似
- 内部工具搜索更看重响应速度
所以索引参数调优,最后总会回到业务容忍度。
4.4 如果过滤是主路径,ANN 就不是一个“纯向量问题”
很多团队离线测索引时只跑:
query vector -> top-k
但真实系统里经常是:
query vector + tenant + acl + doc_type + status + date_range
这时性能和召回不只取决于 ANN 本身,还取决于:
- 过滤发生在搜索前、中还是后
- 过滤字段有没有被单独索引
- 高选择性过滤会不会把 ANN 逼成近似全扫描
Azure AI Search 当前 vectorFilterMode 直接区分:
preFilterpostFilterstrictPostFilter
并明确指出它们会在 recall、latency、throughput 上产生不同结果。Weaviate 当前 filtering 文档也明确说明,当过滤足够严格时,会触发 flat-search cutoff,直接切到 brute-force 子集搜索。pgvector 当前 README 则提醒近似索引上的过滤是 index scan 之后 再生效,因此过滤条件可能导致返回结果变少;Qdrant 当前官方资料也反复强调 payload index 对 filtered search 的重要性。
一个更贴近生产的结论通常是:
filter-aware ANN往往比“单纯换个索引名”更影响线上效果
4.5 如果内存才是瓶颈,量化不是“可选优化”,而是路线选择
向量检索上量之后,团队最先撞到的常常不是:
- 单条查询慢一点
而是:
- 索引太占内存
- build 太重
- 多租户一起放不下
Weaviate 当前专门把 vector quantization 拆成:
BQPQSQRQ
Milvus 当前则把 IVF_SQ8、HNSW_SQ、HNSW_PQ、HNSW_PRQ 等压缩路线分成独立索引家族;Qdrant 当前也把 quantization 单列出来,说明量化不是边角功能,而是成本与规模路线的一部分。
所以量化真正回答的问题通常是:
为了把索引放进可接受的内存 / 成本边界里,你愿意损失多少精度,并用多少额外候选或重排把它补回来
5. 距离度量也不是小事
很多团队只关心索引名,忽略了距离度量本身。
常见度量包括:
- cosine similarity
- dot product
- euclidean distance
pgvector 当前 README 明确支持多种距离度量。
这里最重要的工程结论不是背定义,而是:
- 建库和查询阶段要和 embedding 模型的输出假设保持一致
否则你会得到一种“看上去都在查向量,但排序总是怪怪的”的问题。
6. 为什么 ANN 调优一定要有 Exact Baseline
如果没有精确基线,你就很难回答:
- 召回下降到底是索引造成的
- 还是 embedding / chunk / filter / query rewrite 造成的
一个更稳的做法通常是:
- 先在一部分黄金集上跑 exact baseline。
- 记录
Recall@k、Hit@k、top1 命中率。 - 再切 ANN,比较损失和延迟收益。
这样你调的就不是“玄学效果”,而是可解释权衡。
6.1 Exact baseline 最好按 query bucket 分层跑
如果你只拿一个混合大盘平均值做 baseline,很容易把真正的问题冲掉。
更稳的做法通常是至少拆成这些桶:
- 纯语义问题
- 编号 / 错误码 / 人名 / 产品名问题
- 高过滤选择性问题
- 大租户和小租户问题
- 热数据和冷数据问题
因为 ANN 参数带来的收益和损失,经常只在某几个 query bucket 里特别明显。
6.2 过滤型查询应该有自己的 exact baseline
Azure AI Search 当前过滤文档、Weaviate 当前 filtering 文档、pgvector 当前 README 都在指向同一个现实:
- 带过滤的 ANN,不等于不带过滤的 ANN
所以更稳的基线不是只测:
unfiltered Recall@k
而是至少还要测:
filtered Recall@kresult_count under filter高选择性过滤下的 P95
否则你会出现一种典型误判:
- 离线 ANN 看起来很好
- 一加 tenant / acl,线上马上掉结果
7. 调 ANN 索引时最常见的四种误判
7.1 把 chunk 问题误判成索引问题
如果 chunk 太碎或太脏,再强的 ANN 也只能把脏候选找得更快。
7.2 把过滤问题误判成索引问题
很多“召回差”其实是:
- filter 过严
- tenant / acl 错了
- 版本字段把正确文档过滤掉了
7.3 把重排缺失误判成索引问题
索引通常负责候选池,不一定负责最终 top1 最优。
如果候选池里已经有正确项,但最终顺序不稳,更像:
- rerank 问题
- hybrid 融合问题
- 引证组装问题
7.4 把 embedding 变更误判成索引波动
Azure AI Search 当前文档强调查询向量和索引向量应在同一 embedding space。
这意味着如果你换了 embedding 模型却没重建索引,问题根本不在 ANN 参数。
8. 不同系统文档给出的工程启发
8.1 pgvector
pgvector 的价值不只是“Postgres 里也能搜向量”,还在于它让你看见一种非常实用的路线:
- 当你已经强依赖事务、JOIN、结构化查询时,不一定非得先上独立向量数据库
pgvector 当前 README 还把参数和运维边界写得非常直接:
- HNSW 主要看
m、ef_construction、hnsw.ef_search - IVFFlat 主要看
lists、ivfflat.probes - HNSW 通常比 IVFFlat recall / speed tradeoff 更好,但 build 更慢、内存更高
- IVFFlat 没有训练前提时效果容易不稳,尤其是在数据还太少时建索引
这给工程团队一个很实用的调优顺序:
- 先别急着改建索引参数。
- 先扫 query 期参数,例如
ef_search或probes。 - 只在收益证据明确时再动
m、ef_construction、lists这类 build 级参数。
因为 query 参数通常更适合拿来做:
- 灰度
- A/B
- 单查询 override
而 build 参数一旦改动,往往意味着:
- rebuild 成本
- 更长维护窗口
- 更重内存占用
8.1.1 pgvector 对过滤的提醒尤其值得重视
pgvector 当前 README 明确提醒:
- approximate index 上的过滤是在扫描后生效
- 过滤条件可能让返回结果少于预期
- 可以开启 iterative index scans 自动扩大扫描范围
- 对少数值过滤可以考虑 partial index
- 对大量不同值过滤可以考虑 partitioning
这说明在 Postgres 路线里,过滤问题往往不该只靠调 ANN,而是要和:
- 普通列索引
- partial index
- 分区设计
- iterative scans
一起看。
8.2 Pinecone
Pinecone 的文档更强调:
- 数据建模
- metadata
- multitenancy
- production checklist
这提醒我们:
- 对托管向量库来说,生产问题常常比索引名字更早出现
8.3 Weaviate
Weaviate 当前文档把:
flat / hnsw / dynamic- named vectors
- hybrid search
- multi-tenancy
放在同一个体系里,适合用来理解“索引只是检索架构的一部分”。
更进一步说,Weaviate 当前文档给了三个特别实用的启发:
dynamicindex 可以从flat自动切到hnsw,默认阈值是10,000对象。- 对限制很强的 filter,系统可能触发
flat-search cutoff,直接在子集上 brute-force。 - 向量压缩不是一个点状开关,而是一整组
BQ / PQ / SQ / RQ路线。
这三点放在一起看,特别适合帮助团队建立下面这个判断:
不是所有租户、所有 collection、所有过滤条件都应该用同一套 ANN 策略
8.4 Milvus / Qdrant
Milvus 和 Qdrant 的文档都非常适合理解:
- 专用向量检索系统为什么会把索引、过滤、存储和搜索参数拆得很细
对工程团队来说,这类系统的价值常在于:
- 让你更细地控制性能与召回
8.4.1 Qdrant:先把 payload index 想清楚,再谈 HNSW 调优
Qdrant 当前资料里最值得抓住的,不只是 HNSW 本身,而是它把:
- vector index
- payload index
- quantization
- hybrid queries
放在一整条链路里讲。
尤其是 Qdrant 当前 indexing / filtering 文档反复强调:
- 只有 vector index 还不够
- 过滤字段最好建 payload index
- payload index 还能帮助系统估计 filter cardinality,参与 query planning
这件事非常关键,因为很多线上 filtered ANN 问题,首先该问的不是:
HNSW 要不要调
而是:
过滤字段到底有没有被单独索引
另外,Qdrant 当前搜索文档还提到 ACORN search algorithm 这类针对严格过滤的路径,这再次说明:
- 过滤和 ANN 在生产系统里是强耦合问题
8.4.2 Qdrant 的量化启发
Qdrant 当前 quantization 文档说明,量化表示可以:
- 作为快速候选选择层
- 也可以直接参与搜索
这提醒团队一个很实用的现实:
- 量化之后别只看“是不是更省了”
- 还要看是否需要保留原向量做 rescoring / refinement
否则很容易出现:
- 成本降了
- 但 top-k 候选质量已经不够下游 rerank 补救
8.4.3 Milvus:压缩索引家族本身就是路线选择
Milvus 当前资料的价值在于,它把“ANN 是一整个工程面”讲得非常展开。
从官方索引列表看,Milvus 已经不只是:
FLATIVF_FLATHNSW
还包括:
IVF_SQ8IVF_PQHNSW_SQHNSW_PQHNSW_PRQ
这说明 Milvus 特别适合帮助我们理解两层现实:
- 索引选择本身已经内含压缩路线。
- 压缩路线往往还需要 refinement 才能把 recall 拉回来。
换句话说,Milvus 给团队最大的启发不是“可选项很多”,而是:
生产 ANN 不是一个索引类型,而是一条 coarse filtering -> compressed retrieval -> refinement 的分层链路
8.5 Azure AI Search:同一系统里就能做 ANN vs exhaustive 对照
Azure AI Search 当前文档非常适合理解企业检索里 ANN 的另一个现实:
- 你可能要同时面对
HNSW、exhaustive KNN、hybrid、vectorFilterMode、semantic ranker
它的一个很重要的工程启发是:
exhaustive = true不只是查询模式,也很适合拿来做线上对照基线
也就是说,你不一定要单独准备一套完全独立的 exact 离线环境,很多时候可以直接在同一系统里对比:
HNSWexhaustive KNN
再去量化 recall 损失和延迟收益。
另外,Azure 当前过滤文档把 preFilter / postFilter / strictPostFilter 的代价写得很明确:
preFilter通常更保 recall,但高选择性过滤会更耗 CPU / latencypostFilter和strictPostFilter更容易出现 false negatives
所以 Azure 路线里一个很常见的误判就是:
- 以为 filter mode 只是实现细节
实际上它可能直接改变:
- top-k 是否够用
- selective filter 下的丢结果概率
- semantic ranker 前能拿到多少候选
9. 一个更实用的 ANN 调优流程
建议不要直接在线上盲调。
更稳的顺序通常是:
- 固定一批 query 黄金集。
- 固定 embedding 模型和 chunk 策略。
- 用 exact baseline 跑出理论上限。
- 比较不同索引类型或参数下的
Recall@k。 - 再比较 P95 延迟、构建时长和内存占用。
- 最后再看业务侧答案指标是否真的变好。
这样能避免一种常见误区:
- 只因为某个参数让向量召回更快,就误以为整个 RAG 系统更好了
9.1 先调 search 参数,再动 build 参数
一个更稳的顺序通常是:
- 先固定数据与 query bucket。
- 先调 query 期参数,例如
ef_search、probes、oversampling。 - 确认收益之后,再动 build 期参数,例如
m、ef_construction、lists或压缩配置。
因为 query 参数通常更适合:
- 小范围验证
- 租户分层
- 路由策略实验
而 build 参数更接近:
- 结构性变更
9.2 对 IVF,先定 lists,再扫 probes
pgvector 当前 README 给了非常实用的经验起点:
lists可以从rows / 1000或sqrt(rows)这类量级开始probes可以从sqrt(lists)起步
这背后的思路非常重要:
lists更像库级结构probes更像查询期搜索半径
所以很多时候不该一上来就频繁重建 IVF,而是先确认:
- 当前 lists 下,probes 的可接受工作点到底在哪
9.3 量化场景记得留“补精度”的候选空间
不管是 Weaviate 的 quantization、Qdrant 的 quantized representation,还是 Milvus 的 IVF_SQ8 / HNSW_PQ 路线,本质都在提醒:
- 压缩会带来信息损失
因此更稳的经验通常是:
- 压缩索引时适当放大候选池
- 让 rerank / refinement 在更高精度上做最后裁决
如果量化后还把 top-k 压得很死,就很容易出现:
- 搜得很快
- 但正确答案从候选阶段就已经丢了
9.4 过滤型 query 要单独建调优桶
不要把:
- 无过滤 query
- 低选择性过滤 query
- 高选择性过滤 query
放在同一桶里看平均值。
因为 Azure filter mode、Weaviate flat-search cutoff、pgvector iterative scans、Qdrant payload indexes 都在说明:
- 过滤型 query 的最优参数,常常和普通语义 query 不同
9.5 Rebuild 不是事故,而是计划动作
ANN 线上治理里很常见但容易被忽略的一件事是:
- rebuild 不是“出问题才做”
而应该提前规划:
- embedding 升级要不要整库重建
- quantization 切换怎么做双轨
- 索引 build 期间如何降级
- 谁来判断回滚
pgvector 当前 README 甚至直接把 build time、parallel workers、maintenance_work_mem、reindex、vacuum 都写进日常运维路径里,这说明 ANN 索引本身就是需要被运营的生产资产。
10. 至少要观察的 6 个索引指标
10.1 召回指标
Recall@kHit@k- top1 / top3 命中率
10.2 延迟指标
- 平均延迟
- P95 / P99 延迟
- 高过滤条件下的延迟波动
10.3 成本指标
- 内存占用
- 存储占用
- 索引构建时间
- 重建成本
10.4 更新指标
- upsert 吞吐
- 删除完成时间
- 重建窗口长度
10.5 租户指标
- 大租户和小租户是否共享同样代价
- 热租户是否拖垮整体延迟
10.6 业务指标
- 最终答案引用正确率
- 混合检索胜率
- rerank 提升幅度
10.7 过滤与候选充足度指标
- selective filter 下的返回结果不足率
candidate_count / requested_k- iterative scan 触发率
- brute-force cutoff 触发率
- exact 对照下的 filtered false negative rate
11. 常见反模式
- 没有 exact baseline,就直接宣称某种 ANN 参数更好。
- 只在单条查询上“感觉更快”,不看黄金集分布。
- 只看向量召回,不看 metadata filter 对延迟和正确率的影响。
- 索引刚调完就换 embedding,导致结果不可比较。
- 只测冷启动,不测持续写入、删除和重建。
- 过滤慢就先调 HNSW,却没先给过滤字段建 payload / inverted / scalar index。
- 一上量就开量化,却没有给候选放宽或保留 refinement。
- 用 unfiltered recall 代表线上带 acl / tenant 的真实效果。
- IVF 在数据还太少时就建很多 lists,后面再把召回问题怪给算法。
- 把 build 参数当成随手可改的开关,却没有预留 rebuild 和回滚窗口。
12. 实战判断题:什么时候先别碰索引
如果你现在遇到的是下面几类问题,先别急着调 ANN:
- 正确文档根本没入库
- chunk 设计明显不合理
- metadata 字段不完整
- namespace / tenant / acl 有串数风险
- 纯向量本来就不适合当前 query 类型
这些问题没先收住,索引优化通常只会让错误更稳定。
13. 推荐搭配阅读
14. 重点官方资料
以下资源已按 2026-07-08 复核可访问:
- pgvector README
- Weaviate Filtering
- Weaviate Vector Quantization
- Milvus Index Explained
- Milvus Indexes supported in Milvus
- Milvus Similarity Metrics
- Milvus IVF_SQ8
- Milvus HNSW_PQ
- Qdrant Indexing Concepts
- Qdrant Indexing
- Qdrant Quantization
- Qdrant Search Concepts
- Weaviate Vector Index
- Weaviate Vector Config
- Azure AI Search Vector Relevance and Ranking
- Azure AI Search Vector Query Filters
- Azure AI Search Hybrid Search Overview