一、先明确问题:HNSW 到底要解决什么?
在 RAG 系统中,文档通常先被切分成 Chunk,再由 Embedding 模型转换为向量。查询到来后,系统需要从大量文档向量中找出与查询最相关的 Top-K 个候选。
设数据库中有 (N) 个向量,每个向量维度为 (d)。最直接的方案是 Flat Search(暴力精确检索):计算查询向量与所有 (N) 个向量的距离,再选出最小的 K 个。
距离计算工作量大致随 增长。它并非"无用的老方案":Flat Search 结构简单、结果精确,适合小数据集、批量矩阵计算、GPU 检索,以及作为近似检索的评测基准。但当数据量很大、查询延迟预算有限时,逐一比较所有向量可能过于昂贵。
HNSW 属于 近似最近邻搜索(Approximate Nearest Neighbor,ANN) 算法。它提前构建一张由向量节点组成的多层图,查询时只探索其中一部分节点,尽可能快速地找到真正近邻。
这里有一个必须先记住的区别:
- 精确检索:保证返回目标距离度量下真正的 Top-K,但可能需要检查大量甚至全部向量。
- 近似检索:试图用较少的距离计算找到足够好的 Top-K,通常能大幅降低搜索成本,但不保证每次都找到精确答案。
因此,HNSW 的目标不是"用数学保证把复杂度变成对数级",而是利用可导航的图结构,在实际数据上减少搜索工作量,并通过参数控制搜索范围与召回率。
二、先建立直觉:为什么用图,而不是只按距离排序?
2.1 把向量看成地图上的地点
可以把每个向量想象成地图上的一个地点。两个向量距离越近,表示它们在选定距离度量下越相似。HNSW 为部分地点建立道路,查询时从一个入口出发,沿道路寻找更接近目标的地点。
不过,这里有两个重要限制:
- 向量空间通常是高维空间,不是人能直接看见的二维地图。
- "相似的点之间有道路"不等于"只走最近的一条路就一定能到达全局最近点"。
如果所有道路都只连接极近的邻居,图可能形成许多局部紧密的小团块,跨区域导航会很困难。如果道路连接过少,图又可能不够连通或不够容易导航。因此,图的连接质量是 HNSW 性能的关键,而不仅仅是层数。
2.2 小世界:不是"最多六跳"
"小世界"通常用来描述网络中节点之间能够通过相对较短的路径连接。HNSW 借助邻居连接与分层导航,尝试让搜索能从图的一处快速移动到目标附近。
社会学中的"六度分隔"可以帮助理解这个概念,但它不是 HNSW 的算法定理。HNSW 没有"最多六跳就能找到答案"的固定保证。实际需要访问多少节点,取决于数据分布、图质量、搜索参数和实现细节。
三、HNSW 的整体结构:底层负责覆盖,高层负责导航
HNSW 全称为 Hierarchical Navigable Small World,即分层可导航小世界图。
它由多层图组成:
- 第 0 层(底层):包含所有向量节点,承担最终的近邻搜索。
- 第 1 层及更高层:只包含被随机分配到这些层的部分节点,越高层通常越稀疏。
- 入口节点(entry point):查询通常从当前索引的最高层入口开始。
- 边(edge):连接节点的图边,表示搜索可以从一个节点跳到另一个节点。边并不一定意味着两份文本在语义上"互相理解",它首先是一种服务于导航的索引结构。
可以把它想象成地图中的多级道路:
- 底层像街道,覆盖所有地点,便于在目标附近精细搜索;
- 高层像快速路和高速公路,只经过少数节点,帮助跨越较远区域;
- 从高层到低层时,搜索位置逐步细化。
读图要点: 高层节点是底层节点的子集;查询从稀疏的高层开始导航,再逐层进入包含更多节点的底层。图中连线只用于展示层级与连接概念,不代表真实数据中的边分布。
这个比喻能解释设计意图,但不要把它理解为每次查询都能精准"飞抵目标区域"。高层只是提供更有效的起点和导航路径,仍可能把搜索带到并非最优的区域。
四、核心之一:节点为什么要随机分层?
4.1 随机层级分配
插入新向量时,HNSW 通常会按某种指数衰减分布随机决定它能出现的最高层级。经典形式可写为:
其中:
- (U) 是取值在 ((0,1)) 内的均匀随机变量;
- () 是控制层级分布的参数;
- (L) 是新节点被分配的最高层级。
这里的 () 与连接参数 M
不是一回事。具体实现可能使用不同的参数约定或等价形式,应以对应实现为准。
这种分布的关键性质是:层级越高,节点越少,而且数量大致按指数速度下降。 在上述连续分布近似下:
因此,较高层节点会越来越稀疏,形成层级结构。最高层的期望高度通常随节点总量呈对数级增长。
4.2 为什么不直接把最重要的节点放到高层?
HNSW 一般不需要先理解每个向量的语义重要性,也不必全局聚类后再决定谁是"枢纽"。随机分层可以用较低的维护成本形成稀疏导航层。
注意:高层节点并不必然是业务上最重要、最热门或最具代表性的文档。它们是为了索引导航而被随机选中的节点。
4.3 随机分层不等于性能保证
对数级的层高描述的是层级结构的统计性质,并不能单独推出整个查询过程严格为 ()。查询还要在每层访问多个节点;数据分布、连接质量和搜索宽度都会影响实际开销。
应避免的误解:"节点分层后,数据量增加 100 倍,查询延迟也一定只增加固定的几毫秒。"这不是 HNSW 的通用保证,必须通过目标数据和硬件上的基准测试验证。
五、核心之二:HNSW 如何插入一个新节点?
插入过程不是把新节点直接扔进某一层,然后只连接附近几个点。经典流程可以拆成以下步骤。
5.1 第一步:决定新节点的最高层
算法先为新节点随机抽取最高层级 (L)。假设新节点的最高层是第 2 层,那么它会出现在第 2、1、0 层,不会出现在第 3 层及以上。
5.2 第二步:从入口开始逐层导航
从当前索引的最高层入口出发,算法在高于新节点最高层的层级上进行搜索,找到更适合进入下一层的候选位置。
例如,新节点只存在于第 0 层,而索引最高层是第 4 层,那么算法可以先在第 4、3、2、1 层进行导航,再进入第 0 层进行邻居搜索。
在高层,经典 HNSW 插入流程通常使用较窄的贪心搜索:反复检查当前节点的邻居,如果发现距离新节点更近的节点,就移动过去;当邻居中没有更近的候选时,停止这一层的移动,并把得到的位置带到下一层。
这就是"上一层领路,下一层接着找"的来源。但更准确的说法是:上一层给下一层提供搜索起点,而不是把新节点准确定位到了最终位置。
5.3 第三步:在新节点所在层及以下层寻找候选邻居
当搜索到新节点最高层 (L) 后,算法开始在 (L) 到第 0
层寻找可连接的邻居。与高层导航不同,这一阶段通常会使用更宽的候选搜索,其候选规模由
efConstruction 等参数控制。
候选搜索不是只沿着单一路径前进。它会维护待探索的候选节点,并持续从中选择当前较有希望的节点,检查其邻居,直到满足停止条件。
可以把它想象成在地图上寻找附近的道路接入点:
- 只看眼前一个地点,容易错过另一条更合适的路线;
- 适度扩大候选范围,可以发现更好的接入点;
- 候选范围越大,通常越有机会构建高质量的图,但建索引所需的距离计算和时间也会增加。
5.4 第四步:选择邻居,而不是机械地取最近的 M 个点
这是理解 HNSW 时特别重要、也经常被科普文章略过的部分。
假设新节点周围有以下候选:
- A:离新节点最近;
- B:与 A 几乎在同一个方向,距离也很近;
- C:稍远一些,但位于另一个方向;
- D:更远一些,能通往另一个区域。
如果只取距离最近的 M 个节点,最终连接可能大量集中在 A、B 这样的局部区域。虽然每条边都很短,但图未必容易从其他区域导航过来。
HNSW 的经典邻居选择启发式会考虑候选之间的相对距离,尽量避免所有连接都指向相互高度冗余的近邻。在一些常见实现中,候选点与新节点之间的距离,以及候选点与已选邻居之间的距离,都会影响是否保留这条连接。
直觉上,这有点像选道路接入点:不仅要考虑"离我近不近",还要考虑"这些道路是否都通往同一个方向"。保留一定的方向多样性,可能帮助搜索更容易跨越局部区域。
这只是对启发式目标的解释,并不意味着 HNSW 能保证图的全局最优。具体邻居选择规则应以论文和实现源码为准。
5.5 第五步:建立连接,并可能调整旧节点的邻接表
新节点被选入邻居集合后,算法会建立相应的图连接。经典实现中通常会把新节点与选中邻居连接,并在邻居一侧更新连接关系。
但一个节点的邻接表有容量上限。加入新连接后,旧节点可能超出该层允许的连接数,因此算法可能需要重新筛选其邻居,移除部分旧边。
所以,下面这种说法不准确:
新节点绝不会打扰或重新排布其他老节点。
更准确的是:
HNSW 插入通常只修改局部图结构,不需要对整个索引全局重建;但为了满足连接上限和邻居选择规则,局部旧节点的邻接关系可能被调整。
5.6 M 为什么会影响图质量?
M
控制每个节点可保留的邻接连接规模,但具体含义依实现而异。常见实现可能对底层与上层使用不同的连接上限,例如底层上限与
2M 相关、上层上限与 M 相关;不能将其视为所有数据库都统一采用的规则。
一般来说:
M较小:图占用内存较少、构建可能更快,但连接不足可能影响可导航性和召回率。M较大:图拥有更多连接,可能改善搜索质量,但会增加图内存、构建成本和查询时需要检查的邻居数量。
M
不是越大越好。最佳值依数据集、距离度量、召回目标、内存预算和引擎实现而定。
六、核心之三:查询时究竟发生了什么?
理解查询过程,需要区分高层的贪心导航与底层的候选集搜索。
6.1 高层:贪心导航
给定查询向量 (q),算法从最高层入口节点开始。在当前层,它计算当前节点邻居与 (q) 的距离:
- 如果某个邻居比当前节点更接近 (q),就移动到该邻居;
- 如果没有邻居能进一步改善距离,就结束当前层搜索;
- 把当前得到的节点作为下一层的起点。
这种策略简单、快速,适合在稀疏高层上逐步靠近查询区域。
但贪心搜索有局限:它只接受眼前能改善距离的移动,可能停在局部最优点。如果通往真正最近邻的路径需要先经过一个暂时更远的节点,单纯贪心就可能错过它。
6.2 底层:为什么不能只沿着一条路走?
第 0 层包含全部节点,最终 Top-K 通常在这一层搜索得到。经典 HNSW 的底层搜索一般会维护几个逻辑集合:
- 待探索候选集:已经发现、但其邻居还可能值得继续检查的节点。
- 当前结果集:当前最好的候选结果,通常限制在一定规模内。
- 已访问集合:避免反复访问同一个节点。
算法不断从待探索候选中取出当前距离查询向量较近的节点,检查它的邻居,并把有希望的节点加入候选与结果集合。当候选中最值得探索的节点已经不可能改善当前结果集时,或者达到实现定义的停止条件时,搜索结束。
这里的"结果集"在搜索过程中不一定就是最终返回给用户的 Top-K。它可能保留比 K 更多的候选,用来避免过早丢弃有价值的搜索路径;结束后再从结果中选出需要的 K 个结果。
6.3 efSearch:决定搜索视野有多宽的关键参数
efSearch
通常用于控制查询阶段的搜索宽度或候选规模。具体实现可能在细节上有所不同,但调优方向通常是:
- 较小的
efSearch:检查节点较少,通常更快、资源消耗更低,但可能错过真正近邻。 - 较大的
efSearch:探索更多候选,通常有机会提高召回率,但会增加距离计算、CPU 消耗和查询延迟。
efSearch 与最终返回的 K 不是同一个参数。比如请求
Top-10,内部搜索可以探索远多于 10 个候选节点。若搜索过程中只保留 10
个节点,可能过早丢弃有价值的替代路径。
可以用"侦察范围"来理解它:K 是最终要带回多少个结果,efSearch
是搜索过程中愿意调查多大范围。两者相关,但不能互相替代。
6.4 为什么 HNSW 会漏掉真正的最近邻?
可能原因包括:
- 图的连接质量不足,通往目标区域的路线不理想;
efConstruction较小,构建时没有充分探索候选,导致图质量受限;M较小,连接数不足;efSearch较小,查询时探索不够;- 数据分布、过滤条件或实现细节使导航更困难。
因此,HNSW 返回的是基于当前图和搜索配置找到的近似结果。它不会因为使用了分层结构就自动保证精确 Top-K。
七、三个参数如何一起影响结果?
HNSW 的常见核心参数可以按作用阶段理解:
| 参数 | 主要作用阶段 | 调大后的常见影响 | 主要成本 |
|---|---|---|---|
M | 图构建与图结构 | 连接更丰富,可能改善可导航性与召回率 | 图内存、构建与查询开销 |
efConstruction | 插入建图 | 邻居候选探索更充分,可能形成更好的图 | 建索引更慢、构建计算量更大 |
efSearch | 查询 | 搜索更广,通常有机会提高召回率 | 查询延迟与 CPU 消耗增加 |
三者并不是可以简单互换的旋钮:
M决定图结构能保留多少连接;efConstruction决定建图时有多认真地寻找连接对象;efSearch决定查询时愿意探索多少候选。
如果图本身质量较差,只把 efSearch 调得很大可能仍无法完全弥补。反过来,构建一个高质量图,也不意味着可以在极低搜索宽度下始终获得高召回。
常见的调优方法是先固定数据集、距离度量与评测集,再分阶段调参,而不是同时改变三个参数后凭感觉判断。
注意: 上图只是说明常见趋势的概念示意,不是任何数据库的实测结果。实际曲线可能因数据、硬件和实现不同而明显变化。
八、如何正确评估 HNSW:速度快不等于检索好
8.1 用 Flat Search 建立精确基准
要评估近似检索的召回效果,需要在相同数据集、向量、距离度量、过滤条件与查询集上,用精确检索获得基准 Top-K。然后比较 HNSW 返回的结果。
常见指标是 Recall@K:
其中:
- () 是 ANN 返回的 K 个结果;
- () 是精确检索返回的 K 个结果。
例如,精确检索的 Top-10 与 HNSW 的 Top-10 有 9 个结果重合,则该查询的 Recall@10 为 0.9。实际评测通常需要在大量代表性查询上统计平均值或分布,而不是只看单个样本。
Recall@K 衡量的是结果与精确近邻集合的重合程度,不等于业务语义相关性本身。一个系统即使准确复现了向量距离排名,也可能因为 Embedding 模型、文本切片或数据质量的问题而返回不相关内容。
8.2 不要只报告平均延迟
企业级检索至少应该关注:
- Recall@K:近似结果与精确基准的重合程度;
- P50 / P95 / P99 延迟:典型及尾部请求耗时;
- QPS / 吞吐量:给定并发和硬件下每秒可处理的查询量;
- 索引构建耗时:初次建图与增量写入的成本;
- 内存占用:向量、图连接、元数据和运行时开销;
- 更新可见性与恢复能力:写入多久可查、进程重启后如何恢复。
如果只报告"毫秒级",读者无法知道这是 1 毫秒还是 90 毫秒,也不知道它对应多少数据、多少并发、怎样的召回率。
8.3 建议的基准测试流程
- 固定一份代表性数据集和查询集,记录向量维度、数量、距离度量及数据来源。
- 用 Flat Search 为每条查询生成精确 Top-K。
- 构建 HNSW,逐步调整
M与efConstruction,记录构建时间和索引内存。 - 对每一种索引配置,改变
efSearch,统计 Recall@K、P50、P95、P99 和 QPS。 - 在目标并发量下重新测试,避免把单请求延迟当成生产吞吐能力。
- 加入真实的元数据过滤、权限约束、更新与删除操作,检查线上工作负载是否改变结果。
- 选取满足召回、延迟和内存约束的配置,而不是盲目追求单项最优。
九、企业知识库落地:HNSW 只是检索链路的一部分
9.1 内存预算不能只计算向量
假设有 100 万条向量,每条 1536 维,采用 float32,每个维度占 4 字节:
这约为 6.14 GB(十进制)或 5.72 GiB(二进制) 的原始向量数据。这个数字还没有包括图连接、节点元数据、原始文本、ID 映射、分配器开销、缓存以及数据库自身的内存开销。
因此,不能直接用"向量数 × 维度 × 4 字节"当成 HNSW 的完整内存预算。实际容量应通过目标引擎、目标参数和目标数据集测量,并为峰值构建、合并、缓存及并发请求预留空间。
9.2 PB 级数据不等于单个 HNSW 图可以轻松装下
PB 级通常是整个系统的数据存储规模,不意味着单个 HNSW 索引能把全部向量放入内存并轻松完成查询。超大规模系统通常需要考虑分片、数据分区、冷热分层、分布式路由和多阶段召回。
分片可以降低单个索引的规模,但也带来额外问题:查询要访问哪些分片、如何合并各分片结果、如何平衡热点,以及全局 Top-K 的质量如何评估。不能只凭 HNSW 单机算法就推导出 PB 级系统的整体性能。
9.3 元数据过滤与权限隔离
企业知识库往往需要按租户、部门、文档类型、时间或访问权限过滤。过滤和 ANN 的组合方式会影响召回率与延迟:
- 有些系统会在 ANN 搜索过程中应用过滤条件;
- 有些场景可能先做近似召回,再过滤候选;
- 也可能需要分区、预过滤或其他混合策略。
如果先取少量近邻再过滤,可能出现大量候选被过滤掉,最终不足 K 条结果;如果为了补足结果不断扩大搜索范围,延迟又可能上升。
权限控制必须由可靠的数据访问机制保障,不能把"向量相似"当作用户有权查看文档的依据。过滤策略要用真实权限分布进行测试,不能只用无过滤的基准数据推断线上表现。
9.4 RAG 不只有向量检索
HNSW 负责的是向量近邻搜索,并不直接解决以下问题:
- 文档切片是否合理;
- Embedding 是否能表达业务语义;
- 关键词、精确实体和编号是否需要额外检索;
- 重排模型是否能把真正有用的证据排在前面;
- 生成模型是否忠实使用检索证据。
因此,实际 RAG 链路可能组合向量召回、关键词召回、元数据过滤和 Rerank。最终答案的可靠性还取决于数据质量、检索覆盖率、证据组织与生成策略。HNSW 能提高检索效率,但不能保证"零幻觉"。
9.5 写入、刷新与持久化要看具体引擎
不能笼统地认为 Bulk 写入、异步写入或调整刷新间隔对所有向量引擎都具有相同效果。不同系统的索引构建、事务、可见性、后台合并、磁盘持久化和并发控制机制可能差别很大。
工程验证时应明确:
- 使用哪个引擎及版本;
- 是批量初始导入还是持续增量写入;
- 写入后多久必须可查询;
- 查询和写入同时发生时,延迟如何变化;
- 删除、更新、重启与故障恢复如何处理;
- 索引重建期间是否需要额外内存与磁盘空间。
对于 Elasticsearch、Milvus、Infinity 等不同系统,具体配置名称、默认值和行为都应以对应版本的官方文档为准,不能把某个引擎的参数定义直接套到另一个引擎上。
十、常见误区总结
误区 1:HNSW 查询复杂度一定是 (O(log N))。
层级高度通常随数据规模对数增长,但实际搜索还取决于每层访问节点数、图结构和搜索参数。应报告实测性能,而不是给出没有适用条件的复杂度保证。
误区 2:HNSW 总能找到真正的 Top-K。
它是近似最近邻算法,可能漏掉精确近邻。应使用 Recall@K 对照精确基准评估。
误区 3:M 越大,召回率一定越高。
更大的连接规模可能改善图质量,但效果取决于数据和实现,同时会增加内存及计算成本。
误区 4:efSearch 就是最终返回的 K。
K 是输出结果数量,efSearch 通常控制查询阶段的搜索宽度,两者作用不同。
误区 5:每个节点只需要连接最近的 M 个节点。
经典 HNSW 会使用邻居选择启发式,避免连接完全退化为局部、冗余的近邻集合。
误区 6:插入新节点不会影响旧节点。
插入通常只修改局部图结构,但可能调整邻居的连接关系。
误区 7:HNSW 解决了 RAG 的准确性问题。
HNSW
解决的是向量近似检索效率问题。语义相关性、权限隔离、证据质量和生成可靠性仍需要整条系统链路共同保障。
结语
HNSW 的关键不只是"把向量连成图",而是把三个机制结合起来:
- 随机分层:形成节点逐层稀疏的导航结构;
- 邻居选择与局部连接维护:让图在连接规模有限的条件下尽量保持可导航性;
- 分层导航与底层候选搜索:在有限计算预算内探索尽可能有价值的候选节点。
它的工程价值来自可调节的速度、召回率与资源之间的权衡,而不是无条件保证对数复杂度、亚毫秒延迟或精确 Top-K。真正的企业级落地,必须把算法理解、参数实验、容量规划、过滤与权限策略、并发写入和故障恢复放在同一个系统视角下评估。