2467 words
12 minutes
向量数据库检索算法详解

向量数据库检索算法详解#

向量检索是现代 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×√nn 为向量总数,中心数约为 4 倍平方根
nprobe搜索探查簇数10-100越多召回率越高,但延迟增加

2.3 PQ 参数#

参数含义推荐值说明
m分段数32-128分段越多精度越高,但编码长度增加
nbits每段编码位数8通常为 8,即 256 个码字

2.4 LSH 参数#

参数含义推荐值说明
nbits哈希位数维度 2-4 倍维度为向量维度
ntables哈希表数量50-200越多召回率越高,内存占用越大

三、算法选型决策树#

根据不同业务场景选择合适的算法:

数据量 < 100万,精度优先
└── 推荐 HNSW
数据量 100万-1000万,均衡考虑
└── 推荐 HNSW 或 IVF+PQ
数据量 > 1000万,内存受限
└── 推荐 IVF + PQ
稀疏向量 / 去重任务
└── 推荐 LSH
需要增量更新 + 大规模
└── 推荐 IVF(可叠加 PQ)

四、主流产品算法对比#

产品主要算法特点
PineconeHNSW + 优化托管服务,开箱即用
MilvusHNSW、IVF、GPU加速开源灵活,支持混合索引
WeaviateHNSW默认 HNSW,支持矢量化和混合搜索
FaissIVF、PQ、HNSW、LSH 全支持Facebook 出品,算法最全面
QdrantHNSW + 滤波支持带过滤条件的向量检索
ChromaHNSW(基于 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 稀疏检索#

┌─────────────────────────────────────────────────────────────────┐
│ RAG 检索架构 │
│ │
│ Query ──► ┌─────────────┐ │
│ │ Retrieval │ │
│ └──────┬──────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Sparse │ │ Dense │ │
│ │ Retrieval │ │ Retrieval │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ▼ ▼ │
│ BM25 / TF-IDF Embedding Model │
│ (词项匹配) (语义匹配) │
└─────────────────────────────────────────────────────────────────┘

核心区别

┌────────────┬──────────────────────────────┬───────────────────────────────┐
│ 维度 │ 稀疏检索 (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 检索流程:

用户问题 → ┌─────────────────────────────────────────┐
│ 1. Sparse: BM25 初步召回(关键词匹配) │
│ → 快速召回 100 个候选 │
│ │
│ 2. Dense: Embedding 精排(语义相似度) │
│ → 从 100 个中选出 top-5 │
│ │
│ 3. Rerank: 交叉编码器重排 │
│ → 最终 top-3 输出 │
└─────────────────────────────────────────┘
相关文档 context
LLM 生成回答

何时使用哪种检索

┌──────────────┬──────────┬───────────────────────────────────┐
│ 场景 │ 推荐方式 │ 原因 │
├──────────────┼──────────┼───────────────────────────────────┤
│ 关键词明确 │ 稀疏检索 │ "Python async await" 精确词项匹配 │
├──────────────┼──────────┼───────────────────────────────────┤
│ 同义词查询 │ 稠密检索 │ "心脏" = "心肌" 语义理解 │
├──────────────┼──────────┼───────────────────────────────────┤
│ 多语言场景 │ 稠密检索 │ 跨语言 embedding │
├──────────────┼──────────┼───────────────────────────────────┤
│ 企业内部术语 │ 混合检索 │ 兼顾专有名词 + 语义 │
├──────────────┼──────────┼───────────────────────────────────┤
│ 实时性要求高 │ 稀疏检索 │ 计算更快 │
└──────────────┴──────────┴───────────────────────────────────┘

八、总结#

向量检索算法的选择需要综合考虑以下因素:

  1. 数据规模:决定采用单一算法还是混合方案
  2. 精度要求:HNSW 精度最高,LSH 适合快速过滤
  3. 内存限制:PQ 可大幅压缩内存占用
  4. 查询延迟:实时系统需要 O(log n) 级别的响应
  5. 更新频率:IVF 支持增量更新,HNSW 需要重建

对于大多数场景,HNSW 是首选方案;若数据量超过千万级或内存受限,可考虑 IVF+PQ 组合。具体参数设置建议从推荐值开始,通过实际测试调整到最优配置。

向量数据库检索算法详解
https://sgjki547.top/posts/向量数据库检索算法详解/
Author
SGJki
Published at
2026-04-09
License
CC BY-NC-SA 4.0