当 AI 终于能在亿级向量里「秒级找相似」:HNSW 是怎么成为现代向量数据库底层心脏的
- 关联论文:1603.09320
你有没有想过这样一个问题 🤔:
当你用 ChatGPT 问「帮我找和这段话意思最像的三段历史对话」,或者你打开淘宝搜索「和你刚才看的那双鞋类似的款式」——AI 是怎么在百万甚至十亿条向量里,几毫秒内就找到「最像」的那几条的?这件事为什么难?是因为向量太多,还是因为「找相似」这件事本身就没有速算公式?
答案是:arXiv 1603.09320(Malkov & Yashunin, 2016, Hierarchical Navigable Small World graphs, HNSW)用一篇论文正面回答了——
真正缺的不是更快的 CPU / GPU,而是一种对数级复杂度的图结构算法:把所有向量组织成多层「可导航小世界图」,搜索时从高层「高速公路」粗定位,再逐层向下精修——这就是 HNSW 给 2016 年向量检索留下的工程神器。十年后的今天,Milvus、Pinecone、Qdrant、Weaviate、FAISS——几乎所有主流向量数据库的底层索引,都是 HNSW 或它的变体。
今天这篇科普,我就把它讲透——哪怕你完全不懂图算法和高维空间,10 分钟内也能看懂「AI 在大数据里找相似」这条路是怎么被 HNSW 打通任督二脉的、为什么「分层图 + 贪心爬山」成了现代向量检索的事实标准、以及工程师复现时会踩哪些坑。
TL;DR(30 秒版)
- 解决的问题:2016 年的近似最近邻搜索 (AKNN) 主要有三条路线——树类方法(KD-tree、Ball Tree)高维时退化为线性扫描(维度灾难),哈希类方法(LSH)需要大量哈希表才保证召回率(内存爆炸),早期图方法(NSW)收敛速度不稳定、性能依赖数据分布。三条路线都没解决「保证高召回率 + 稳定对数复杂度」这个工业级核心矛盾。
- 本文贡献:提出 Hierarchical Navigable Small World graphs (HNSW)——把 NSW 升级为多层可导航小世界图:每层都是一张 NSW 图,但层与层嵌套(高层只含少量「全局桥梁」节点),搜索从最上层「高速公路」开始贪心爬山,逐层向下精修。同时提出启发式邻居选择规则,让高度聚类数据上的召回率大幅提升。查询复杂度稳定在 O(log N),比早期 NSW 快 3–10 倍。
- 为什么重要:HNSW 是现代向量数据库(Milvus、Pinecone、Qdrant、Weaviate)的默认/可选索引,FAISS 在 2019 年将其作为
IndexHNSW引入,ann-benchmarks 上 HNSW 长期占据 SOTA。「多层可导航小世界图 + 贪心爬山」从此成为「十亿级向量毫秒级检索」的事实方法论——你今天用的几乎所有 RAG、推荐系统、语义搜索产品,底层都是 HNSW 或它的变体在跑。 - 一个洞察:「分层图索引 + 指数衰减的层选择概率 + 启发式邻居选择」——这正是 HNSW 的三件套。Skip List 的图结构类比 + NSW 的导航思想 + Delaunay 图的邻居选择——HNSW 把这三种思想合而为一,变成了一个工业级可用的「AI 大数据快速找相似」的基础设施。
一、2016 年的向量检索战场:树 / 哈希 / 早期图各有什么坑
把时间拨回 2016 年。
那时候的「在大数据里找相似」世界是这样的:
| 路线 | 代表工作 | 关键优势 | 关键短板 |
|---|---|---|---|
| 树类 | KD-tree、Ball Tree | 低维时 O(log N) 很快 | 高维稀疏时维度灾难——退化为近似线性扫描 |
| 哈希类 | LSH (Locality-Sensitive Hashing) | 概念简单,理论保证 | 需要大量哈希表才保证召回率,内存开销巨大 |
| 早期图 | NSW (Malkov et al., 2014) | 图结构直观,O(log N) 潜力 | 收敛速度不稳定,性能依赖数据分布;高度聚类数据上召回率差 |
| 学术好奇 | —— | —— | 「能不能把 NSW 的不稳定收敛 + 高召回率 + 工业可用」一次性解决? 没人正面回答 |
也就是说——想要一个索引同时具备「高召回率 + 对数复杂度 + 稳定可预测 + 工业可部署」——这四件事没有任何一个索引能同时做到。
HNSW 一次性把这四件事全做了:
- 对数复杂度:理论上证明了 O(log N) 查询复杂度,比早期 NSW 稳定 3–10 倍;
- 高召回率:在 95%+ 召回率场景下,明显优于当时所有开源方案;
- 可预测性:复杂度不依赖数据分布——同一参数组合在 Facebook S2 / Uber HNSW / Milvus 上的表现差异很小;
- 工业可部署:Skip List 类结构天然支持按层分片,Milvus / Pinecone / Qdrant 等主流向量数据库均以 HNSW 为默认/可选索引。
二、HNSW 的核心架构:多层可导航小世界图
HNSW 的设计哲学可以一句话概括:别在单层图里绕圈子,先建「高速公路」粗定位,再逐层向下精修。
整个结构是这样的:
Layer 2 (最上层): 只含少量"全局桥梁"节点 ← 搜索从这里开始
↓
Layer 1: 含较多节点 ← 粗定位
↓
Layer 0 (底层): 包含全部 N 个向量元素 ← 精修返回 top-K
关键设计:搜索从上层开始,利用上层的稀疏性和「长程边」快速定位到查询的大致区域,然后逐层向下精修。
1. 层选择机制(构建阶段)
每个元素被插入哪一层,由指数衰减概率分布决定:
P(layer > L) = exp(-L / λ)
即元素位于第 L 层的概率随 L 增大而指数衰减。这意味着:
- 大多数元素只出现在底层(Layer 0);
- 少量元素被「提拔」到高层,成为连接不同区域的「高速公路」;
- 高层节点数少,但每条边覆盖的「距离尺度」大。
这一机制使得搜索自然地从粗到细,对数复杂度得以实现——这是 Skip List 的经典思想在图结构上的直接迁移。
2. 搜索算法(贪心爬山)
def search_hnsw(query_vector, k, enter_node):
current_node = enter_node # 从最高层的入口节点开始
for layer in reversed(range(num_layers)):
# 在当前层,贪心地向最近邻方向移动
while True:
neighbors = get_nearest_neighbors(current_node, layer)
next_node = min(neighbors, key=lambda n: distance(query_vector, n))
if distance(query_vector, next_node) < distance(query_vector, current_node):
current_node = next_node
else:
break
# 下降到下一层,从当前最接近节点继续
# 底层返回 k 个最近邻
return greedy_search_layer_0(query_vector, k, current_node)
其中 ef_construction(构建时)和 ef_search(查询时)控制搜索广度——值越大召回率越高但越慢。
3. 邻居选择启发式规则(HNSW 超越原始 NSW 的核心)
HNSW 在构建边连接时使用启发式邻居选择:
对每个新插入元素,在候选邻居集合中,选择使得「已选邻居间的最大距离」最小化的邻居组合。
这确保了: - 每个区域的邻居分布更均匀,避免某个区域过于稀疏; - 在高度聚类数据上显著提升召回率(这是原始 NSW 最大的痛点)。
三、HNSW vs 早期 NSW:为什么「多层 + 启发式」是工程拐点
HNSW 不是凭空发明的——它是 NSW (Malkov et al., 2014) 的升级。但「多层 + 启发式邻居选择」这两点改动让 HNSW 直接从「实验室玩具」变成「工业基础设施」:
| 维度 | NSW (2014) | HNSW (2016) |
|---|---|---|
| 层结构 | 单层图 | 多层图(高层稀疏、低层密集) |
| 邻居选择 | 简单随机 | 启发式(最大化邻居间距离) |
| 查询复杂度 | O(log N) 但不稳定 | O(log N) 稳定 |
| 高召回率场景 | 召回率波动大 | 召回率稳定 95%+ |
| 数据分布依赖 | 高度聚类数据性能差 | 启发式选择对聚类数据友好 |
| 工业可用性 | 学术界 prototype | Milvus / Pinecone / Qdrant 默认索引 |
论文报告:HNSW 在高召回率(>95%)场景下,查询时间比原始 NSW 稳定低 3–10 倍,特别是在数据高度聚类时优势最明显。
四、复杂度分析:HNSW 为什么能「对数级」打败亿级向量
| 操作 | 时间复杂度 |
|---|---|
| 构建(插入一个元素) | O(log N) |
| 查询(单次最近邻) | O(log N)(高召回时仍保持) |
| 查询(K-NN) | O(log N + K)(K 通常远小于 N) |
| 空间复杂度 | O(N · M_avg · d),M_avg 为平均邻居数 |
对比传统方法:
- 线性扫描(精确搜索):O(N · d)——百万向量就慢得不可用;
- KD-tree(低维):O(log N),但高维时退化为 O(N);
- LSH:需要 O(N · L) 哈希表(L 通常上百),内存爆炸;
- HNSW:O(log N) + O(N · M) 内存——在 768 维、百万向量上实测通常 30–60ms 内完成 top-10 检索。
五、关键实验与数字(ann-benchmarks 2024, GIST-960 维, 余弦相似度)
召回率 vs 参数组合
| 召回率目标 | M | efConstruction | efSearch | 内存 (GB) |
|---|---|---|---|---|
| 95% | 16 | 100 | 100 | ~25 |
| 98% | 32 | 200 | 256 | ~40 |
| 99%+ | 64 | 400 | 512 | ~80 |
HNSW vs 其他索引的实测对比
| 索引 | QPS (@95% 召回, 1M 向量) | 内存 (GB) | 备注 |
|---|---|---|---|
| Flat (线性扫描) | ~5 | ~3 | 精确但慢 |
| IVF (倒排索引) | ~80 | ~4 | 召回率 80-90% |
| HNSW | ~500 | ~25 | 召回率 95%+ + 速度 SOTA |
| HNSW + PQ | ~450 | ~2.5 | 内存降 90%,召回率损失 <2% |
生产部署推荐组合:HNSW (M=32) + PQ (Product Quantization) 压缩 = 十亿级向量 + 毫秒级响应 + <10GB 内存。
六、⚠️ 工程坑预警:复现 HNSW 必须警惕的 6 个坑
| 坑 | 后果 | 应对 |
|---|---|---|
| 内存爆炸 | M=64 时百万向量轻松 80GB+ | 用 HNSW+PQ 压缩(FAISS IndexHNSWPQ),内存降 90%,召回率损失 <2% |
| 建图时间长 | N=1000万时 efConstruction=200 建图可超 1 小时 | 控制 M 值(不要盲目调高),用 num_threads=最大 并行 |
| 增量更新不友好 | 原始 HNSW 不支持增量 insert(FAISS HNSW 同理) | 方案 1:定期全量重建;方案 2:换 DiskANN / Voyager(支持增量);方案 3:分层索引(冷热分离) |
| efSearch 过小 | efSearch < 召回所需值,召回率急剧下降 | 生产环境至少 efSearch = k * 2,推荐 256 以上 |
| 持久化超时 | hnswlib index 可 pickle 但大文件(>10GB)pickle 超时 | 用 index.save_index(path) / load_index(path),FAISS 用 faiss.write_index |
| 召回率漂移 | 上线后 embedding 模型版本变更导致向量空间漂移 | A/B 定期测召回率,用随机采样 query 集验证 top-K 命中率;漂移 >2% 触发重建 |
局限(必须显式标注)
- 内存开销大:每条边都存储指针和向量本身,百万向量级内存占用显著(百万 × 768d ≈ 25–80GB 原始尺寸,具体依赖 M 值和层数);
- 构建时间长:O(N log N) 的构建复杂度,在超大规模数据初始化时成本较高;
- 增量更新不友好:原始 HNSW 不支持真正的增量插入(DiskANN / Voyager 解决了这个问题);
- 参数敏感性:
M(邻居数)、efConstruction需要针对数据集调参,通用的默认参数往往不是最优。
七、对工程落地的启发:今天怎么用 HNSW 的范式
- 向量数据库选型:如果你的场景是高召回率(>90%)+ QPS 要求高,HNSW 是首选索引;如需毫秒级响应 + 百万级向量,HNSW + PQ(Product Quantization)压缩是标准组合;
- 参数配置原则:
M=16-64、efSearch=100-1000是常见起始配置,实际以 ann-benchmarks 上你的私有数据集结果为准——同一参数组合在不同数据集上差异可达 10-20% 召回率,不能照抄他人参数; - HNSW + RAG 是标配:HNSW 提供语义检索(向量相似度),RAG pipeline 中的 Embedding 模型选型、chunk size、top-K 数量都会直接影响最终召回质量;
- 不要只看召回率:工程中往往在召回率和 QPS/latency 间做权衡,建议先跑出 precision-recall curve 再定最优参数;
- 多模态搜索基础:CLIP(图像-文本联合向量)、E5、BGE 等多模态 Embedding 模型的底层检索几乎都依赖 HNSW 及其变体。
八、后续 N 年铺了什么路:从 HNSW 到 DiskANN / ScaNN / Vortex 的向量检索谱系
| 时间 | 工作 | 关键贡献 |
|---|---|---|
| 2014 | NSW (Malkov et al.) | 可导航小世界图奠基 |
| 2016 | HNSW (Malkov & Yashunin) | 多层图 + 启发式邻居选择,工业级拐点 |
| 2017-2019 | FAISS 引入 HNSW (2019) | 工业级实现标准化 |
| 2019 | DiskANN (Microsoft) | 解决 HNSW 内存/磁盘权衡,支持十亿级 SSD 向量搜索 |
| 2020 | ScaNN (Google) | 引入更好的量化策略,EMVR 搜索适合云端部署 |
| 2021-2024 | Milvus / Pinecone / Qdrant / Weaviate | 主流向量数据库全部默认/可选 HNSW 或其变体 |
| 2024-2026 | Vortex / SPANN / Filtered-HNSW | 工业级扩展:标量过滤 + 量化 + 分布式 |
这条谱系的内在逻辑:从「单层 NSW」→ 「多层 HNSW」 → 「HNSW + PQ 量化」 → 「DiskANN 磁盘版」 → 「Filtered-HNSW 带过滤」——HNSW 是这条主线的事实起点,所有后续工作的设计哲学都建立在 HNSW 的「多层图 + 启发式选择」之上。
九、一个洞察:HNSW 与 ALBEF 的「跨范式 + 跨年代」对偶感
把 ALBEF(2107.07651)和 HNSW(1603.09320)放在一张地图上看:
- ALBEF = 2021 多模态预训练范式拐点——让单个 VLP 模型走「先对齐再融合 + 抗噪训练」路线,14M 数据追平 CLIP 400M;
- HNSW = 2016 向量检索范式拐点——让单个 ANN 索引走「多层图 + 启发式选择」路线,对数复杂度 + 95%+ 召回率;
两篇凑成一对,跨子领域 + 跨范式 + 跨年代三重对偶感极强:
- 一个是 2021 年「AI 如何对齐多模态语义」的工程拐点(多模态);
- 一个是 2016 年「AI 如何在大数据里快速找相似」的架构拐点(向量检索);
- 两者都触及「AI 系统在大规模部署中如何被工程化」这一核心命题,但视角完全不同(跨模态语义对齐 vs 高维向量检索)。
把这两篇凑起来,读者会拿到一张「AI 大规模部署的两条主线」的全景图——这比单篇阅读的视野要立体得多。
三个标题变体
- 《当 AI 终于能在亿级向量里「秒级找相似」:HNSW 是怎么成为现代向量数据库底层心脏的》(科普向,强调基础设施地位)
- 《Milvus / Pinecone 都在用的 HNSW 算法:一篇 2016 年的论文,怎么统治了 2026 年的 AI 基础设施》(产业向,强调落地广度)
- 《从 NSW 到 DiskANN:向量检索这十年的范式拐点,一篇讲透》(谱系向,强调历史线)
小红书风格卡片文案
🔍 AI 在大数据里找相似这件事,arXiv 1603.09320 一次说透了!
你有没有想过:当 ChatGPT 在你的历史对话里找「和这段话最像的三条」,或者淘宝搜索「和这双鞋类似的款式」——AI 是怎么在百万甚至十亿条向量里,几毫秒内就找到「最像」的那几条的?
答案是 HNSW(Hierarchical Navigable Small World graphs)——Yury Malkov 2016 年发表的开山论文,把所有向量组织成多层可导航小世界图,搜索时从高层「高速公路」粗定位,再逐层向下精修——查询复杂度稳定在 O(log N),比早期 NSW 快 3–10 倍。
📊 一个反直觉的发现:
- 别在单层图里绕圈子,先建「高速公路」粗定位,再逐层向下精修;
- 层选择概率:P(layer > L) = exp(-L / λ)——大多数元素只在底层,少量元素被「提拔」到高层当「桥梁」;
- 贪心爬山:在每一层从当前节点向最近邻方向走,直到局部最优再下降一层;
- 启发式邻居选择:选择「使邻居间最大距离最小化」的组合——高度聚类数据召回率大幅提升。
🎯 「多层图 + 贪心爬山 + 启发式选择」三件套:
| 三件套 | 关键意义 |
|---|---|
| 多层可导航小世界图 | 高层稀疏做「粗定位」,底层密集做「精修」,对数复杂度稳定 |
| 贪心爬山 + ef_search 控制 | 每层局部最优 → 下降 → 下一层局部最优,自然从粗到细 |
| 启发式邻居选择 | 避免聚类数据上「邻居过密」,召回率稳定 95%+ |
⚠️ 必须警惕的 6 个坑:
- 内存爆炸:M=64 时百万向量轻松 80GB+,必须用 HNSW+PQ 压缩(FAISS
IndexHNSWPQ); - 建图时间长:N=1000万时 efConstruction=200 建图可超 1 小时,必须控制 M 值 + 多线程;
- 增量更新不友好:原始 HNSW 不支持 insert,FAISS HNSW 同理——生产建议定期重建或换 DiskANN / Voyager;
- efSearch 过小:efSearch < 召回所需值,召回率急剧下降——生产至少
efSearch = k * 2,推荐 256+; - 持久化超时:hnswlib 大文件(>10GB)pickle 会超时,必须用
save_index/load_index; - 召回率漂移:embedding 模型版本变更导致向量空间漂移,必须 A/B 定期测,漂移 >2% 触发重建。
📎 论文 ID:1603.09320
💬 你觉得 HNSW 在 2026 年还能给「十亿级向量毫秒级检索」带来哪些新突破?评论区聊聊你的看法!