← blog AI Agent · 2026-04-09
vector-db-retrieval-algorithms
深入解析主流向量数据库检索算法,包括HNSW、IVF、PQ、LSH等,涵盖算法原理、参数调优、选型决策及代码实战
向量检索是现代 AI 应用的核心技术之一,广泛应用于语义搜索、推荐系统、图像检索等场景。本文将深入解析主流向量数据库采用的检索算法,并提供实用的选型和配置指南。
一、常用检索算法分类
1.1 HNSW (Hierarchical Navigable Small World)
HNSW 是一种基于多层图结构的近似最近邻搜索算法,被视为当前最流行的向量索引算法。
核心特点:
- 多层图结构,自顶向下构建稀疏连接
- 查询速度:O(log n),适合实时检索场景
- 召回率:95-99%,精度表现优异
- 内存占用较高,以空间换时间
工作原理: HNSW 构建一个多层图,上层节点连接较远,下层节点连接较近。查询时从最上层开始,通过贪心搜索快速定位,逐步向下层细化,最终找到最近邻。
1.2 IVF (Inverted File Index)
IVF 采用倒排索引思想,先将向量空间聚类划分,查询时只需搜索部分聚类中心。
核心特点:
- 先聚类后搜索,搜索范围可控
- 支持增量索引,无需全量重建
- 召回率取决于探查簇数量
- 可与 PQ 结合进一步压缩
1.3 PQ (Product Quantization)
Product Quantization 将高维向量分割成多个子空间,分别进行量化编码。
核心特点:
- 高压缩比:10-50x 内存压缩
- 支持亿级向量规模
- 查询速度受分段数影响
- 常与 IVF 结合使用(IVF-PQ)
1.4 LSH (Locality-Sensitive Hashing)
LSH 通过哈希函数将相似向量映射到相同桶中。
核心特点:
- 适合稀疏向量检索
- 天然支持去重任务
- 需要较多哈希表达到高精度
- 查询召回率相对较低
1.5 HNSW + IVF 混合
结合两者优点,在 HNSW 图结构上叠加 IVF 索引层,进一步提升大规模数据下的检索效率。
二、核心参数详解
2.1 HNSW 参数
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
| M | 每节点最大邻居数 | 16-64 | 注意:指每个节点最多连接的节点数,而非每层节点数 |
| efConstruction | 构建时搜索广度 | 100-400 | 值越大构建越慢,但索引质量越高 |
| efSearch | 查询时搜索广度 | 50-500 | 值越大召回率越高,但查询延迟上升 |
参数调优建议:
- 数据量小、精度优先:M=32-64,efConstruction=200-400
- 数据量大、追求速度:M=16-32,efConstruction=100-200
- 查询时 efSearch 可动态调整,无需重建索引
2.2 IVF 参数
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
| nlist | 聚类中心数 | ~4×√n | n 为向量总数,中心数约为 4 倍平方根 |
| nprobe | 搜索探查簇数 | 10-100 | 越多召回率越高,但延迟增加 |
2.3 PQ 参数
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
| m | 分段数 | 32-128 | 分段越多精度越高,但编码长度增加 |
| nbits | 每段编码位数 | 8 | 通常为 8,即 256 个码字 |
2.4 LSH 参数
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
| nbits | 哈希位数 | 维度 2-4 倍 | 维度为向量维度 |
| ntables | 哈希表数量 | 50-200 | 越多召回率越高,内存占用越大 |
三、算法选型决策树
根据不同业务场景选择合适的算法:
graph TD
A["数据量 < 100万,精度优先"] --> B["推荐 HNSW"]
C["数据量 100万-1000万,均衡考虑"] --> D["推荐 HNSW 或 IVF+PQ"]
E["数据量 > 1000万,内存受限"] --> F["推荐 IVF + PQ"]
G["稀疏向量 / 去重任务"] --> H["推荐 LSH"]
I["需要增量更新 + 大规模"] --> J["推荐 IVF(可叠加 PQ)"]
四、主流产品算法对比
| 产品 | 主要算法 | 特点 |
|---|---|---|
| Pinecone | HNSW + 优化 | 托管服务,开箱即用 |
| Milvus | HNSW、IVF、GPU加速 | 开源灵活,支持混合索引 |
| Weaviate | HNSW | 默认 HNSW,支持矢量化和混合搜索 |
| Faiss | IVF、PQ、HNSW、LSH 全支持 | Facebook 出品,算法最全面 |
| Qdrant | HNSW + 滤波 | 支持带过滤条件的向量检索 |
| Chroma | HNSW(基于 Faiss) | 轻量级,嵌入管理专精 |
五、代码示例:Faiss 实战
Faiss 是 Facebook 出品的向量检索库,提供了所有主流算法的实现。
5.1 HNSW 索引
import faiss
d = 128 # 向量维度
M = 32 # 每节点最大邻居数
# 创建 HNSW 索引
index = faiss.IndexHNSWFlat(d, M)
# 设置参数
index.hnsw.efConstruction = 200 # 构建时搜索广度
index.hnsw.efSearch = 100 # 查询时搜索广度
# 添加向量
index.add(vectors)
# 查询
D, I = index.search(query_vector, k=10)
# D: 距离数组, I: 最近邻索引数组
5.2 IVF 索引
d = 128
nlist = 100 # 聚类中心数
# 使用内积作为相似度度量
quantizer = faiss.IndexFlatIP(d)
# 创建 IVF 索引
index = faiss.IndexIVFFlat(quantizer, d, nlist)
# 必须先训练再添加向量
index.train(vectors)
index.add(vectors)
# 设置探查数量
index.nprobe = 20
# 查询
D, I = index.search(query_vector, k=10)
5.3 IVF-PQ 混合索引
d = 128
nlist = 100 # 聚类数
m = 64 # PQ 分段数
nbits = 8 # 每段编码位数
quantizer = faiss.IndexFlatIP(d)
# 创建 IVF-PQ 索引
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, nbits)
index.train(vectors)
index.add(vectors)
D, I = index.search(query_vector, k=10)
六、配置示例:主流数据库
6.1 Milvus 配置
index:
type: HNSW
params:
M: 32
efConstruction: 200
search:
params:
ef: 100
6.2 Qdrant 配置
{
"hnsw_config": {
"m": 32,
"ef_construct": 200
},
"query_params": {
"hnsw_ef": 128
}
}
6.3 Weaviate 配置
{
"vectorIndexType": "hnsw",
"vectorIndexConfig": {
"ef": 128,
"efConstruction": 200,
"maxConnections": 32
}
}
七、RAG 中的稠密检索 vs 稀疏检索
graph TD
Q["Query"] --> R["Retrieval"]
R --> S["Sparse Retrieval"]
R --> D["Dense Retrieval"]
S --> B["BM25 / TF-IDF<br/>(词项匹配)"]
D --> E["Embedding Model<br/>(语义匹配)"]
核心区别
| 维度 | 稀疏检索 (Sparse) | 稠密检索 (Dense) |
|---|---|---|
| 表示方式 | 高维稀疏向量(词项出现与否) | 低维稠密向量(语义embedding) |
| 匹配方式 | 词项精确匹配 / 统计权重 | 余弦相似度 / 向量距离 |
| 代表算法 | BM25、TF-IDF | Sentence-BERT、ColBERT |
| 同义词处理 | ❌ 无法处理 | ✅ 语义理解 |
| 多语言支持 | 依赖分词器 | ✅ 跨语言 |
| 计算成本 | 低 | 中高 |
| 可解释性 | 高(词项可追溯) | 低(向量不可解释) |
| 冷启动 | ✅ 无需训练 | 需要预训练模型 |
稀疏检索详解
# BM25 算法核心思想
def bm25_score(query: str, document: str, avgdl: float, k1=1.5, b=0.75) -> float:
"""
BM25 = Σ IDF(qi) × (tf(qi,d) × (k1+1)) / (tf(qi,d) + k1 × (1-b + b × |d|/avgdl))
词项出现次数越多分数越高,但有衰减(饱和)
文档越长惩罚越大
"""
score = 0.0
for qi in query.split():
tf = document.count(qi) # 词项频率
idf = log((N - n(qi) + 0.5) / (n(qi) + 0.5)) # 逆文档频率
numerator = tf * (k1 + 1)
denominator = tf + k1 * (1 - b + b * len(document) / avgdl)
score += idf * numerator / denominator
return score
稀疏检索示例:
Query: "Python 异步编程"
Document: "Python asyncio 是异步编程框架"
BM25 匹配:
- “Python” ✓ 匹配
- “异步” ✓ 匹配
- “编程” ✓ 匹配 Score: 高(三个词都命中)
但无法理解 “asyncio” = “异步编程” 的语义关系
稠密检索详解
稠密检索核心思想
def dense_retrieve(query_embedding, document_embeddings, top_k=5):
"""
1. Query → Embedding Model → 向量 q
2. Document → Embedding Model → 向量 d1, d2, ...
3. 计算余弦相似度: cos(q, di)
4. 返回 top-k 最相似的文档
"""
similarities = []
for doc_id, doc_emb in document_embeddings.items():
sim = cosine_similarity(query_embedding, doc_emb)
similarities.append((doc_id, sim))
# 返回最相似的 top-k
return sorted(similarities, key=lambda x: x[1], reverse=True)[:top_k]
稠密检索示例:
Query: "Python 异步编程"
→ Embedding: [0.12, 0.45, 0.78, ...] (768维向量)
Document 1: "Python asyncio 是异步编程框架"
→ Embedding: [0.11, 0.47, 0.79, ...] → similarity: 0.94 ✓
Document 2: "Java 多线程并发处理"
→ Embedding: [0.65, 0.12, 0.08, ...] → similarity: 0.23
即使词项完全不匹配,也能通过语义理解返回正确结果
混合检索(Hybrid Retrieval)
实际应用中通常结合两者优势:
class HybridRetriever:
def __init__(self, sparse_weight=0.3, dense_weight=0.7):
self.sparse_weight = sparse_weight
self.dense_weight = dense_weight
self.sparse_retriever = BM25Retriever()
self.dense_retriever = DenseRetriever()
def retrieve(self, query: str, top_k=10) -> list[Document]:
# 稀疏检索
sparse_results = self.sparse_retriever.retrieve(query, top_k * 2)
# 稠密检索
dense_results = self.dense_retriever.retrieve(query, top_k * 2)
# RRF (Reciprocal Rank Fusion) 融合
fused_scores = {}
for rank, doc in enumerate(sparse_results):
fused_scores[doc.id] = fused_scores.get(doc.id, 0) + self.sparse_weight / (60 +
rank)
for rank, doc in enumerate(dense_results):
fused_scores[doc.id] = fused_scores.get(doc.id, 0) + self.dense_weight / (60 +
rank)
# 返回融合后 top-k
sorted_docs = sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)
return [doc for doc_id, _ in sorted_docs[:top_k]]
在 AI-Interview 项目中的检索方式
RAG 检索流程:
graph TD
用户问题 --> S1["1. Sparse: BM25 初步召回(关键词匹配)<br/>快速召回 100 个候选"]
S1 --> S2["2. Dense: Embedding 精排(语义相似度)<br/>从 100 个中选出 top-5"]
S2 --> S3["3. Rerank: 交叉编码器重排<br/>最终 top-3 输出"]
S3 --> C["相关文档 context"]
C --> L["LLM 生成回答"]
何时使用哪种检索
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 关键词明确 | 稀疏检索 | “Python async await” 精确词项匹配 |
| 同义词查询 | 稠密检索 | “心脏” = “心肌” 语义理解 |
| 多语言场景 | 稠密检索 | 跨语言 embedding |
| 企业内部术语 | 混合检索 | 兼顾专有名词 + 语义 |
| 实时性要求高 | 稀疏检索 | 计算更快 |
八、总结
向量检索算法的选择需要综合考虑以下因素:
- 数据规模:决定采用单一算法还是混合方案
- 精度要求:HNSW 精度最高,LSH 适合快速过滤
- 内存限制:PQ 可大幅压缩内存占用
- 查询延迟:实时系统需要 O(log n) 级别的响应
- 更新频率:IVF 支持增量更新,HNSW 需要重建
对于大多数场景,HNSW 是首选方案;若数据量超过千万级或内存受限,可考虑 IVF+PQ 组合。具体参数设置建议从推荐值开始,通过实际测试调整到最优配置。