千万级文档怎么秒搜?向量数据库 + ANN
吴恩达新课《RAG》解读 · 第 4 篇(共 10 篇)· 硬核篇,含算法
引子:暴力搜索撑不住
上一篇我们说语义搜索靠"向量相近"找文档。最直白的做法是:把提问也变成向量,和知识库里每一个文档向量都算一次距离,排序取最近的 K 个。
这就是 k-近邻搜索(k-Nearest Neighbors, kNN)。它简单可靠,但有个致命问题——扩展性极差:
• 1000 份文档 → 每次查询算 1000 次距离,还行 • 10 亿份文档 → 每次查询算 10 亿次距离,比前者慢 100 万倍
线上系统要求几百毫秒内响应,kNN 在这种规模下直接出局。怎么办?用"近似"换"速度"——这就是 ANN。
一、ANN:不追求最优,只追求"足够近"
ANN(Approximate Nearest Neighbors,近似最近邻) 是一族算法的统称。它们的核心交易是:
牺牲一点点结果质量(不保证找到绝对最近的,但找到的非常近),换取巨大的速度提升。
这个交易在 RAG 场景下几乎总是划算——你只需要"足够相关"的文档喂给 LLM,差个千分之一秒级别的精度,没人感知得到。
ANN 算法有很多,今天生产系统里绝对的主流是 HNSW。我们重点讲它。
二、先理解"邻近图"(Proximity Graph)
HNSW(Hierarchical Navigable Small World,分层可导航小世界)的基础是邻近图。建索引时:
1. 把每个文档向量看成一个节点 2. 给每个节点连几条边,连向离它最近的几个邻居 3. 整体形成一张网状图
查询时,不用和所有文档比距离,而是在图上"跳":
1. 随机挑一个起点节点 2. 看它的几个邻居,谁离查询向量最近就跳过去 3. 重复,直到没有邻居比当前更近 → 返回
每一步只看几个邻居,计算量极小。就像问路——不用走遍全城,只要每一步都朝目标方向更近一点走,很快就能到达附近。
代价:它是贪心的,每步只看局部最优,可能错过全局更近的某个点。但实践证明,找到的已经足够近。
三、HNSW:给邻近图"加分层",更快
HNSW 在邻近图基础上加了分层,把搜索的前期"大跨度赶路"做得更高效:
• 第 1 层(最底):包含所有向量,建完整的邻近图 • 第 2 层:随机保留一部分(比如 1/10),再建图 • 第 3 层(最顶):再随机保留一小撮(比如 1/100),再建图
搜索时从最顶层开始:
1. 在又稀又糙的顶层做大跳跃,快速进入目标"大致区域" 2. 下探一层,节点更密,继续逼近 3. 一路下探到第 1 层(全量),精细微调,返回最近邻
类比高铁通勤:顶层像坐高铁跨省,中层像坐地铁跨城,底层像步行找门牌——先坐高铁再换地铁再步行,比全程步行快太多。
四、为什么是"对数级"复杂度
每往上一层,节点数呈指数减少(比如每层只保留上一层的 1/M)。于是从顶层走到底层,跳的层数是 O(log N) 级别,而不是 kNN 的 O(N)。
这就是为什么向量搜索能扩展到几十亿向量还很快的数学原因。
五、ANN 的几个关键特性
实操中你不需要自己实现 HNSW(向量数据库都内置了),但要记住几点:
1. 快:比 kNN 快得多,让大规模向量搜索可行 2. 近似:不保证找到绝对最佳,但通常足够好 3. 依赖建图:邻近图构建是一次性重活儿,但可以预先建好,查询时直接用
还有更激进的量化方案(二值量化、Matryoshka 嵌入等)能进一步压缩、加速,我们讲"成本与延迟"那篇会细聊。
六、向量数据库:把这一切打包好
向量数据库(vector database)就是专门为存向量、跑 ANN 优化过的数据库。它干这几件事:
• 存高维向量数据 • 内置 ANN 索引(如 HNSW)的构建和查询 • 还能同时跑关键词搜索、元数据过滤,组合成 hybrid search • 提供增删改、权限、多租户等数据库级能力
课程里用的是 Weaviate(开源),同类还有 Milvus、Qdrant、Pinecone、pgvector 等。它们 API 各异但核心能力相通——选哪个更多看生态和部署偏好。
一个典型的 Weaviate 用法(伪代码):
# 1. 建集合,指定嵌入模型collection = client.collections.create( name="Article", vectorizer_config=Configure.Vectorizer.text2vec())# 2. 批量导入文档(自动向量化 + 建 HNSW 索引)with collection.batch.dynamic() as batch:for doc in documents: batch.add_object(properties=doc)# 3. 混合搜索(关键词 + 语义)result = collection.query.hybrid( query="温哥华酒店为什么贵", alpha=0.25, # 0=纯关键词, 1=纯语义, 这里偏关键词 limit=3)注意 alpha 参数:它控制 hybrid search 里关键词和语义的权重比例——上一章说的 beta,在不同库里名字不同,本质一样。
小结与预告
这一篇我们啃下了 RAG 检索的"性能命门":
• kNN 暴力搜索在亿级文档上撑不住 • ANN 用"近似"换"速度",主流算法是 HNSW • HNSW 靠分层邻近图把复杂度压到对数级 • 向量数据库把存向量、跑 ANN、混合搜索打包成现成能力
但还有一个看似不起眼、却深刻影响检索质量的问题:文档那么长,到底该切成多大块再去做向量? 切太大向量糊成一团,切太小又丢失上下文。下一篇我们就讲分块(chunking)的艺术。
本文基于 DeepLearning.AI《RAG》课程整理。下一篇《文档切多大才好搜?分块的艺术》即将更新。
夜雨聆风