现在搭建一个 RAG 系统,很多向量数据库会默认提供 HNSW 索引。把文档向量写进去,然后调用 search 拿回 Top-K,的确很方便。但如果内存不够怎么办?数据量只有几万,还需要建图吗?界面上的 M、ef_search、nprobe 又分别是什么?
这些问题最后都会回到同一个取舍:我们愿意花多少时间和内存,换取多高的召回率。向量索引并没有脱离场景的最优解。小规模知识库可以直接使用精确搜索;数据量和并发量上来之后,再用近似搜索交换召回率、延迟与内存。
所以本文不打算给 ANN 算法排一个名次,而是从最简单的精确搜索开始,看看 IVF、PQ 和 HNSW 分别在哪里节省了成本,又因此付出了什么。
向量检索在 RAG 中的位置
向量检索先用 Embedding 模型将文档片段和用户问题映射到同一个高维空间,然后寻找与查询向量最接近的文档向量。它解决的是召回问题,而不是整个 RAG。检索出来的候选项还可以与 BM25 结果融合,再经过 Reranker 排序,最后才会进入大模型的上下文。
ANN 索引所做的事情其实非常有限:在时间和资源预算内,尽可能复现精确搜索的 Top-K 结果。它不能自动修复下列问题:
- Embedding 模型没有将相关查询和文档映射到附近;
- 文档切块破坏了完整语义;
- 查询需要精确匹配产品编号或错误代码;
- Reranker 或大模型未能正确使用已召回的证据。
这个边界很重要。当精确向量搜索都无法找到目标文档时,更换 HNSW 参数或购买更昂贵的向量数据库并不会从根本上解决问题。
从精确最近邻搜索开始
先看最笨的方法。假设数据库中有 个 维向量,我们计算查询向量与每个库内向量的相似度,再选出分数最高的 个。如果使用点积,距离计算的主要复杂度为 。这就是 Exact Search,没有任何花巧,也不会漏掉真正的近邻。
对已归一化的向量 和 ,余弦相似度与点积等价:
同样在归一化条件下,欧氏距离与余弦相似度也可以相互转换:
选择余弦还是欧氏距离,取决于向量是否归一化、Embedding 模型的训练目标和索引实现。实践中应遵循模型提供方的相似度定义,并确认向量数据库中的 Metric 与之一致。
精确搜索的优点是简单且确定。再加上现代 CPU 的向量化指令、高度优化的矩阵运算和 GPU,暴力搜索并没有名字听起来那么笨。Faiss 同时提供精确、近似和压缩检索,这也说明不同索引并非互相取代,而是一组连续的工程取舍。
对于规模较小或查询量较低的知识库,精确搜索可能就是最合理的生产方案。即使最终必须使用 ANN,精确搜索也应当作为评测基线,用来衡量近似索引究竟遗漏了多少近邻。
ANN 的本质是可控的不完整搜索
数据量继续增长,Exact Search 终究会碰到性能边界。那能不能别查全库,只查可能包含答案的那一小部分?这就是 ANN 方法共有的思路。
不同方法可以压缩向量、划分空间、构造图结构,或者将向量映射到哈希桶中。做法虽然不同,目标都是先生成一个远小于全库的候选集,再在其中搜索近邻。
缩小搜索范围必然会带来遗漏风险。一个查询向量可能位于两个分区的边界,它的真实近邻也可能在另一个分区。因此,理解一种 ANN 索引时,可以始终追问两个问题:
- 它如何排除大部分不可能的向量?
- 它如何降低错过边界附近真实近邻的概率?
没有一种索引能同时将延迟、内存、构建时间和近似误差都降到最低。只有先给定资源和质量约束,才能定义 ANN 索引的最优方案。
IVF:使用聚类中心路由查询
可以把 IVF(Inverted File Index)理解为先分区,再搜索。它首先对向量聚类,将每个向量放到最接近的聚类中心下。查询时,系统先找到与查询最接近的几个 Centroid,然后只检查这些聚类中的成员。这就像寻找一家餐厅时,先定位商圈,再在商圈内寻找,没有必要遍历整座城市。
这种索引的关键参数通常包括:
nlist:索引中倒排列表或聚类的数量;nprobe:每次查询需要检查的聚类数量。
更大的 nprobe 会让查询覆盖更多分区,降低边界遗漏,但也会增加距离计算和数据访问量。如果 nprobe 接近 nlist,索引就逐渐接近全库搜索。
IVF 中的 Centroid 是查询路由器,不是文档的最终替代品。命中候选聚类后,系统仍会搜索簇内的成员向量。后文讨论的语义记忆压缩则会用少量代表替换原始成员,两者解决的问题不同。
PQ:压缩向量而不是删除向量
IVF 解决的是查多少向量,PQ 解决的则是每个向量占多少空间。Product Quantization(PQ)将高维向量拆分成多个子向量,并为每个子空间建立码本。它使用最近码字的编号代替原始浮点数据,把一个原本占用大量字节的向量压缩成一组紧凑代码。
查询时,系统可以预先计算查询子向量到各个码字的距离,再通过查表估算查询与库内向量的距离。它降低了存储和内存带宽压力,代价是量化误差。码字越紧凑,压缩越强,原始向量细节损失往往也越多。
IVF 与 PQ 常组合为 IVF-PQ。IVF 从空间中找到少量候选分区,PQ 压缩分区内的向量并加速距离估算。这个组合适合超大规模、内存受限的检索场景。Faiss 的 GPU 相似性搜索研究也将精确、近似与压缩域搜索放在同一个性能优化框架中讨论。
HNSW:在多层近邻图上导航
前面的 IVF 先将空间分区,HNSW(Hierarchical Navigable Small World)则换了一条思路:它把向量连成一张多层近邻图。上层只有少量节点和长距离连接,负责快速跨越空间;下层节点更密集,负责在局部精细定位。
查询从高层入口节点开始,沿着能使距离不断减小的边迭代前进,再逐层下降。高层类似高速公路,底层类似城市路网。这一层级化结构使 HNSW 可以避免从全库的任意节点开始穷举。HNSW 的原始论文可见 Malkov 与 Yashunin 的工作。
常见实现中需要关注三类参数:
| 参数 | 作用阶段 | 增大后的一般影响 | 主要代价 |
|---|---|---|---|
M | 图结构 | 节点保留更多连接,导航路径更丰富 | 内存与构建成本增加 |
ef_construction | 索引构建 | 构建时考察更多候选邻居,图质量通常提高 | 构建更慢 |
ef_search | 在线查询 | 搜索时保留更大的候选集,召回率通常提高 | 查询延迟增加 |
HNSW 往往能在高召回率下给出很好的查询性能,所以成为许多向量数据库的默认选择。它的主要成本是内存:除了原始向量,系统还需要存储图的多层邻接关系。频繁删除、大批量更新与分布式分片也会让图的维护比单纯的静态查询更复杂。
LSH 与树结构:理解 ANN 的其他路径
Locality-Sensitive Hashing(LSH)使用一组对距离敏感的哈希函数,使相似向量以更高概率落入同一个哈希桶。一张哈希表可能因为查询处于分割边界而遗漏近邻,多张使用不同映射的表则提供了多次命中机会。LSH 的价值在于可以对某些距离度量给出概率保证,但多表结构会消耗额外内存,其实际召回与性能也依赖参数和数据分布。
k-d Tree 等树方法则递归选择坐标维度划分空间,查询到达叶子节点后,再根据当前最优距离判断是否需要回溯其他分支。在低维数据中,这类方法简洁有效;在几百或数千维的文本 Embedding 中,许多坐标分割的排除能力会迅速下降,回溯范围增大,最终可能接近遍历。
它们不一定是文本 RAG 的首选,但展示了 ANN 的两种基本思路:LSH 用多次概率映射补偿单次分割的遗漏,树结构用回溯补偿坐标分割的边界误差。
Centroid 究竟是查询路由,还是语义压缩
与向量聚类有关的讨论容易混淆两个不同的问题。
前面介绍 IVF 时用到了 Centroid,语义记忆压缩也会用 Centroid,但它们做的并不是同一件事。
在 IVF 中,Centroid 只用来判断应当搜索哪些分区。原始向量仍然存在,只是当前查询会跳过大多数不相关分区。这是索引和路由问题。
在语义记忆 consolidation 中,系统会用少量代表向量替代一个聚类中的多个成员。这时减少的不只是查询范围,也是可检索单元的数量。它追问的是:压缩之后,查询是否仍能恢复原有成员的身份与信息。
The Geometry of Consolidation 用簇内平均余弦距离与有效维度分析这一问题。当语义簇足够紧密时,中心向量可能以较低代价概括成员。当成员分布较散时,少量代表则会损失身份区分。该项目因此根据簇的几何特征,在 Centroid 与保留更多残差信息的代表方法之间进行选择。
这一结果提示我们,语义向量的有效结构可能比名义维度更简单,因此在压缩前应先测量簇内分布。但它不能直接证明 Centroid 索引优于 HNSW。两者优化的目标和评测指标都不同。该研究目前在项目中标记为 NeurIPS 2026 投稿,实验结论仍需要在真实语料、查询和下游任务中独立验证。
根据场景选择索引
理解了这些索引的直觉后,选型反而没有那么神秘。不要先问哪个算法更新,先看数据规模、召回率目标、延迟预算、内存上限和更新模式。
小规模或低查询量:先使用 Exact Search
如果 Exact Search 已经能满足 P95 延迟和吞吐量目标,那就直接用它。没有必要为了使用 ANN 而引入 ANN。精确搜索不需要调节召回与速度参数,数据新增和删除也更直接。对小型企业知识库而言,少一层复杂度往往比一个更漂亮的 Benchmark 数字更有价值。
中等规模、内存充足且追求高召回:优先评估 HNSW
HNSW 是通用性很强的起点。通过增加 ef_search,系统往往可以在接受额外查询成本的前提下逼近精确结果。选择它之前,需要估算原始向量、图连接、元数据索引与运行时候选集共同占用的内存,而不是只计算向量文件大小。
规模很大或内存受限:评估 IVF-PQ
当原始向量无法全部驻留在可用内存中,PQ 提供了明确的压缩途径,IVF 则进一步缩小每次查询需要扫描的编码数量。这类方案应当通过不同的 nprobe、码长和候选集大小绘制 Recall–Latency–Memory 曲线,而不是只评估单一参数组合。
语义簇紧密且允许信息压缩:实验聚类代表
如果多个记录表达可合并的同一概念,Centroid、Medoid 或语义摘要可以作为长期语义记忆的压缩方案。这是一项数据治理决策,不只是索引参数。替换原始成员之前,系统必须明确可以接受哪些细节和身份损失。
不要只比较平均查询延迟
如果某个索引每秒可以查一万次,但只能找回一半的真实近邻,这个 QPS 并没有多少意义。反过来,为了将召回率从 99% 提高到 99.9%,却让内存翻倍,也不一定划算。因此,评价一个为 RAG 服务的索引,至少要同时观察以下指标:
- Recall@K:ANN 结果中包含了多少精确 Top-K 近邻;
- P50/P95/P99 延迟:关注长尾查询,而不是只看平均值;
- QPS 与并发度:在给定资源上可以稳定承担多少查询;
- 索引与峰值内存:不仅包括向量,还包括图、码本、元数据和临时候选集;
- 构建与恢复时间:索引需要多久生成,崩溃后需要多久恢复;
- 更新成本:实时新增、删除和元数据过滤如何影响性能;
- 下游质量:近邻召回率的变化是否影响 Reranker 结果和最终答案。
不同近邻对下游任务的价值并不相同。ANN 遗漏的向量可能只是目标文档的重复片段,也可能是支撑正确答案的唯一证据。因此,ANN Recall 是必要的系统指标,但不是最终业务指标。
一套可复现的评测流程
这些指标看起来很多,真正开始测试时却可以归纳成一条很简单的路径:先用 Exact Search 得到正确答案,再看各种 ANN 索引能用多少成本逼近它。完整流程如下:
- 冻结数据与模型:固定语料版本、Chunking、Embedding 模型、向量维度和距离度量;
- 建立精确基线:使用暴力搜索为每个查询生成精确 Top-K;
- 准备真实查询:查询应覆盖常见、长尾、精确标识符、跨语言和模糊表达,而不是只用随机向量;
- 扫描索引参数:对 HNSW 测试多组
ef_search,对 IVF 测试多组nprobe,对 PQ 测试多组码长; - 绘制 Pareto 曲线:展示各方案在 Recall、P95 延迟和内存之间的非支配前沿;
- 加入生产约束:测试元数据过滤、并发请求、数据更新与冷启动;
- 进行端到端评测:在相同的融合、重排序和生成配置下,比较最终答案质量。
单一排名无法完整表达评测结果。一个可操作的结论应该是:在内存不超过给定上限、Recall@20 不低于目标值时,哪组参数能获得最低的 P95 延迟。资源和质量约束使最佳索引具有明确定义。
一个务实的默认选择
如果在项目早期就开始设计复杂的索引架构,很容易在尚未证明存在的性能问题上过度投入。一个更务实的路径是:
| 场景 | 建议起点 | 主要优势 | 主要代价 |
|---|---|---|---|
| 小规模、低并发 | Exact Search | 精确、简单、便于调试 | 扩展性有限 |
| 中等规模、内存充足 | HNSW | 高召回、低延迟、生态成熟 | 图结构占用内存 |
| 超大规模、内存受限 | IVF-PQ | 搜索范围小、压缩率高 | 需要承担分区和量化误差 |
| 具有明确语义簇且允许合并 | 聚类代表或 GAC 类方法 | 减少可检索单元 | 可能损失成员身份与细节 |
先使用 Exact Search 验证 Embedding 与数据质量,再根据实测性能决定是否引入 HNSW。当内存成为主要瓶颈时,再考虑 IVF-PQ。只有在业务语义上允许合并记录,且数据几何特征支持时,才考虑用少量代表向量替代原始成员。
这个顺序未必能得到算法 Benchmark 上最醒目的结果,却能让每一次复杂度增加都对应一个已经量化的瓶颈。
总结
回到文章开头的问题:到底应该选哪种索引?我的答案是,先不选 ANN。用 Exact Search 验证 Embedding 和数据质量,也建立一个可以比较的精确基线。只有当它确实无法满足规模与延迟目标时,再引入 ANN。
到了这一步,取舍就清楚了:IVF 通过聚类路由缩小搜索范围,PQ 通过量化减少存储和距离计算,HNSW 通过多层图导航在高召回率下降低查询成本。每一种方法都在节省某项成本,也都会付出相应代价。
聚类中心作为 IVF 路由器,与它作为语义记忆的压缩代表,是两个不同问题。前者决定当前查询访问哪些原始向量,后者决定哪些原始向量可以消失。忽略这个区别,容易把语义压缩研究误读为对通用 ANN 索引的全面否定。
对 RAG 而言,最合适的向量索引永远不是算法名称最新或默认参数最多的那一个,而是在真实语料和查询上,以可接受的资源成本达到召回与延迟目标的那一个。先建立精确基线,再测量数据和瓶颈,最后才选择复杂度。