Skip to content

向量数据库与检索

一、向量基础

Q1: 什么是向量/Embedding?

Embedding(嵌入/向量化) = 将文本、图像等非结构化数据映射为高维空间中的一个向量(一串数字)。语义相似的内容,向量距离也近。

示例(简化,实际是几百到几千维):

  "人工智能"  → [0.23, -0.15, 0.67, 0.01, ...]
  "机器学习"  → [0.19, -0.12, 0.58, -0.03, ...]  ← 与"人工智能"距离近
  "天气变化"  → [-0.45, 0.81, 0.02, 0.33, ...]  ← 距离远
  "气候变化"  → [-0.42, 0.78, 0.05, 0.29, ...]  ← 与"天气变化"距离近

直觉理解

  • 每一个维度代表一个语义特征(可能是"是否和科技相关"、"是否积极情感"等)
  • 模型学习到的维度是隐式的,我们不需要明确知道每个维度含义
  • 只要保证"相似内容→相似向量"这个映射,就可以用向量做相似度计算

Q2: 相似度计算方法

方法1: 余弦相似度(Cosine Similarity)⭐最常用
  
  cos(θ) = A · B / (|A| × |B|)
  
  范围: [-1, 1]
  1 = 完全相同
  0 = 完全无关
  -1 = 完全相反
  
  优点:不受向量长度影响,只关心方向(语义)
  适用:文本、图像等语义匹配

  
方法2: 欧式距离(Euclidean Distance / L2)

  d = √[(a1-b1)² + (a2-b2)² + ... + (an-bn)²]
  
  范围: [0, ∞),越小越相似
  适用:特征向量(如人脸特征、图像特征)

  
方法3: 点积/内积(Dot Product / Inner Product)

  A · B = Σ ai × bi
  
  如果向量已归一化(长度=1),点积等价于余弦相似度
  范围: [-1, 1](归一化后)
  优点:计算最快

实际选择建议:

场景推荐方法Milvus配置
文本Embedding余弦相似度 (COSINE)metric_type="COSINE"
图像特征欧式距离 (L2)metric_type="L2"
已归一化向量内积 (IP)metric_type="IP"(速度最快)

Q3: 向量维度的选择

模型维度说明
OpenAI text-embedding-3-small1536性价比高,通用
OpenAI text-embedding-3-large3072效果更好,更慢更贵
BGE-small-zh512中文优化,轻量级
BGE-base-zh768平衡选择
BGE-large-zh1024中文效果好
sentence-transformers768英文/通用场景

维度与性能的权衡

维度 ↑ → 表达能力 ↑  但 存储成本 ↑、检索速度 ↓
维度 ↓ → 检索速度 ↑  但 可能损失精度

推荐做法:
  1. 先用1536维(或512/768维的开源模型)
  2. 如果精度不够,再尝试更大维度
  3. 如果存储/速度不够,用PCA降维或切分成更小维度

二、主流向量数据库对比

Q4: 向量数据库选型指南

┌──────────────────────────────────────────────────────────────┐
│                    向量数据库选型矩阵                           │
├────────────┬──────────┬──────────┬──────────┬─────────────────┤
│   产品     │ Milvus  │ Chroma   │ Pinecone │ Weaviate        │
├────────────┼──────────┼──────────┼──────────┼─────────────────┤
│ 类型       │ 自建服务   │ 本地/服务 │ SaaS云   │ 自建服务        │
│ 开源协议   │ Apache 2│ Apache 2│ 商业     │ BSD             │
│ 规模       │ 大规模   │ 中小规模 │ 弹性     │ 中大规模         │
│ 易用性     │ ⭐⭐⭐⭐  │ ⭐⭐⭐⭐⭐ │ ⭐⭐⭐⭐⭐ │ ⭐⭐⭐           │
│ 性能       │ ⭐⭐⭐⭐⭐│ ⭐⭐⭐    │ ⭐⭐⭐⭐⭐ │ ⭐⭐⭐⭐          │
│ 标量过滤   │ ✅强     │ ✅基础    │ ✅强     │ ✅原生GraphQL   │
│ 多租户     │ ✅       │ ✅        │ ✅       | ✅              │
│ GPU加速    | ✅       | ❌        | ✅       | ✅              │
└────────────┴──────────┴──────────┴──────────┴─────────────────┘

具体建议:

场景推荐产品理由
快速POC/原型Chroma零配置、Python友好、本地即可
企业生产Milvus功能全、性能强、社区活跃、可扩展
不想运维Pinecone全托管、SaaS模式、按量计费
需要GraphQL+向量Weaviate原生支持混合查询、模块丰富
简单/研究场景FAISSFacebook开源、纯向量、最灵活

Q5: Milvus快速上手

python
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType

# 1. 连接
connections.connect(host="localhost", port="19530")

# 2. 定义Schema(类似建表)
fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
    FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=2000),
    FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536),
    FieldSchema(name="category", dtype=DataType.VARCHAR, max_length=64),
]
schema = CollectionSchema(fields, "文档知识库")
collection = Collection("documents", schema)

# 3. 创建索引(HNSW是当前主流)
index_params = {
    "index_type": "HNSW",      # 索引类型:Hierarchical Navigable Small World
    "metric_type": "COSINE",   # 相似度度量
    "params": {"M": 32, "efConstruction": 256}  # 索引构建参数
}
collection.create_index(field_name="embedding", index_params=index_params)

# 4. 插入数据
embeddings = model.encode(["文档1的内容", "文档2的内容", ...])
data = [
    ["文档1的内容", "文档2的内容", ...],  # content
    embeddings,                              # embedding
    ["技术", "技术", ...],                   # category
]
collection.insert(data)

# 5. 加载到内存 + 检索
collection.load()

search_params = {"metric_type": "COSINE", "params": {"ef": 64}}
results = collection.search(
    data=[query_embedding],    # 查询向量
    anns_field="embedding",    # 在哪个字段上搜索
    param=search_params,
    limit=5,                   # 返回Top-5
    expr="category == '技术'",  # 额外的标量过滤
    output_fields=["content"]  # 返回内容字段
)

# 6. 查看结果
for hit in results[0]:
    print(f"ID: {hit.id}, Score: {hit.score}, Content: {hit.entity.get('content')}")

Q6: Chroma快速上手(更简单)

python
import chromadb

# 1. 创建Client
client = chromadb.Client()
# 或持久化到磁盘: client = chromadb.PersistentClient(path="./chroma_db")

# 2. 创建Collection(相当于表)
collection = client.create_collection(
    name="documents",
    metadata={"hnsw:space": "cosine"}  # l2 / ip / cosine
)

# 3. 添加文档(Chroma自动调用Embedding)
collection.add(
    documents=["文档1内容", "文档2内容", "文档3内容"],
    metadatas=[{"source": "guide"}, {"source": "api"}, {"source": "faq"}],
    ids=["doc1", "doc2", "doc3"]
)

# 4. 查询
results = collection.query(
    query_texts=["如何安装Python?"],
    n_results=2,
    where={"source": "guide"}  # 元数据过滤
)

# 5. 删除
collection.delete(ids=["doc3"])

三、索引类型与参数

Q7: HNSW索引详解(最常用)

HNSW (Hierarchical Navigable Small World) = 基于多层小世界图的近似最近邻搜索

搜索原理(简化理解):

Layer 2 (最上层,稀疏节点):  ○        ○
                                ╱        ╲
Layer 1:                     ○──○──○    ○──○
                             ╱        ╲  ╱
Layer 0 (最下层,全量节点):  ●●●●●●●●●●●●●●

搜索过程:
1. 从Layer 2的随机入口点开始
2. 在当前层贪心找到最近邻
3. 下到下一层,从刚才的最近邻继续搜索
4. 重复直到Layer 0
5. 得到最终的Top-K近邻

HNSW参数调优:

参数推荐范围影响
M8-64每层节点的连接数。M越大,召回率越高、内存越大、构建越慢
efConstruction100-500构建时的搜索广度。越大质量越高、构建越慢
ef (搜索时)10-2000搜索时的广度。越大召回率越高、搜索越慢

经验配置:

内存敏感 → M=16, ef=50   (速度快,精度略低)
平衡场景 → M=32, ef=64   (⭐推荐默认)
精度优先 → M=64, ef=128  (高精度,速度/内存开销大)

Q8: 其他索引类型

索引全称特点适用场景
FLAT暴力搜索100%召回、最慢小数据集(<1M)、需要绝对精度
IVF_FLAT倒排文件先聚类后搜索中大数据集、内存充足
IVF_SQ8倒排+标量量化向量压缩到8bit,内存1/4大数据集、内存受限
IVF_PQ倒排+乘积量化向量压缩到更小超大数据集、可容忍精度损失
HNSW多层小世界图高维、高性能、内存较大⭐ 大多数生产场景
DISKANN磁盘友好内存占用极低,依赖SSD超大规模、内存受限

Q9: 索引构建 vs 搜索时间

索引类型    构建时间   搜索延迟   内存占用   召回率

FLAT         最快       最慢       中        100%
IVF_FLAT     快         快         中        95-99%
IVF_SQ8      中         快         低        90-95%
HNSW         中         极快       偏高       95-99%
DiskANN      慢         中         极低      90-98%

四、向量检索实战技巧

Q10: 混合检索(关键词 + 向量)

纯向量检索的问题:关键词匹配能力弱(如"iPhone 15 Pro Max"可能被"Apple手机"匹配,但精确型号不匹配)。用BM25+向量的混合检索可以兼顾。

python
# 1. 向量检索(语义相关)
vector_results = collection.search(
    data=[query_embedding],
    limit=20,  # 多取一些,后面重排
    ...
)

# 2. 关键词检索(精确匹配)
keyword_results = bm25_search(query, documents, top_k=20)

# 3. 融合(RRF - Reciprocal Rank Fusion)
def rrf_score(rank, k=60):
    return 1 / (k + rank)

scores = {}
for rank, doc_id in enumerate(vector_results):
    scores[doc_id] = scores.get(doc_id, 0) + rrf_score(rank)
    
for rank, doc_id in enumerate(keyword_results):
    scores[doc_id] = scores.get(doc_id, 0) + rrf_score(rank)

# 4. 返回融合后的Top-K
top_docs = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:5]

Q11: 文档切分策略与Chunk大小

Chunk大小选择指南:

  小 Chunk (100-200 tokens)
    ✓ 更精确的匹配
    ✓ 检索速度快
    ✗ 丢失上下文
    ✗ 需要更多Chunks
    ➜ 适用:问答系统、关键词密集的内容

  中 Chunk (300-500 tokens) ⭐推荐
    ✓ 平衡精度与上下文
    ✓ 通用性好
    ➜ 适用:大多数文档、通用RAG场景

  大 Chunk (800-1500 tokens)
    ✓ 包含完整上下文
    ✗ 可能引入噪声
    ✗ 检索变慢(向量更长)
    ➜ 适用:长文章、需要完整段落理解


切分策略:
  1. 按段落切分(优先)
  2. 按句子切分(补充,处理长段落)
  3. 固定长度切分(最通用的fallback)
  4. 按标题层级切分(Markdown/HTML,效果最佳)

重叠大小(Overlap):
  · 建议为chunk_size的10-20%
  · 例:chunk=500,overlap=50-100
  · 保证相邻chunk有上下文连续性

Q12: 标量过滤与混合查询

向量搜索的同时,可能需要按时间、分类、来源等做过滤

python
# Milvus示例:先过滤再搜索
results = collection.search(
    data=[query_vec],
    anns_field="embedding",
    param={"metric_type": "COSINE", "params": {"ef": 64}},
    limit=5,
    
    # 标量过滤表达式(类似SQL WHERE)
    expr="""
        category == '技术文档' 
        AND created_at > '2024-01-01'
        AND view_count > 100
    """,
    
    output_fields=["title", "content", "category"]
)

# Chroma示例:用where过滤
results = collection.query(
    query_texts=[query],
    n_results=5,
    
    # where过滤
    where={"category": {"$eq": "技术文档"}},
    
    # 或更复杂的过滤
    where_document={"$contains": "Python"}  # 包含特定关键词
)

Q13: 多向量策略(提高召回的高级技巧)

策略1: 一个文档生成多个向量

  文档:"Python 3.12新特性解析"
  向量1: [基于标题生成]
  向量2: [基于摘要生成]
  向量3: [基于关键词生成]

  分别存储,查询时取并集
  
  优点:召回率更高
  缺点:存储成本×N,检索需要去重


策略2: HyDE(Hypothetical Document Embedding)

  用户查询:"Python asyncio如何使用?"

  LLM生成"假设性文档"(一段看起来像答案的文本)

  用这段文档做向量检索(比直接用问题检索更准确)

  返回真实文档


策略3: 查询改写

  原始查询 → LLM改写为3种不同表达方式 → 3个查询并行检索

  结果合并去重后返回
  
  (类似搜索引擎的"相关搜索"扩展)

五、性能优化

Q14: 大规模场景优化

┌────────────────────────────────────────────────────────────┐
│                   向量检索性能优化                           │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  📊 数据规模:10万以下 → Chroma / FAISS                    │
│           10万-1亿  → Milvus / Weaviate                   │
│           1亿以上    → Milvus集群 + GPU加速                │
│                                                            │
│  🚀 检索优化                                               │
│    · 合理设置ef搜索参数(64-256,越大越准越慢)           │
│    · 用分区(Partition)按时间/类别分片                    │
│    · 启用标量索引(如category字段建B树索引)               │
│    · 预热:定期加载热门collection到内存                    │
│                                                            │
│  💾 内存优化                                               │
│    · 使用IVF_SQ8/PQ等压缩索引(内存减到1/4-1/8)           │
│    · 合理设置M值(HNSW)                                   │
│    · 冷数据可以降维到更小维度                              │
│                                                            │
│  🔄 缓存策略                                               │
│    · 热门Query的Embedding缓存                              │
│    · 热门Query的检索结果缓存                               │
│    · LRU淘汰策略                                           │
│                                                            │
│  📈 监控指标                                               │
│    · 检索延迟(P50/P95/P99)                               │
│    · 召回率(抽样对比ground truth)                        │
│    · 内存/CPU/GPU利用率                                    │
│    · 索引构建时间与大小                                    │
│                                                            │
└────────────────────────────────────────────────────────────┘

Q15: 常见问题排查

现象可能原因解决方法
搜索结果不相关Embedding模型不匹配任务换更大的Embedding模型/微调
搜索太慢(>1s)数据量大 + 索引参数不合理减小ef / 建分区 / 加缓存
内存不够维度太高 + 数据太多量化/降维 / IVF_PQ索引
搜索波动大索引不稳定重新构建索引 / 调整ef
中文效果差用了英文Embedding模型换BGE/M3E等中文专用模型

向量检索高频问题速查

问题一句话答案
用什么向量数据库?原型Chroma,生产Milvus,不想运维Pinecone
用什么相似度?文本用COSINE,特征用L2,已归一化用IP
Chunk大小?300-500 tokens + 10-20% overlap(通用)
Embedding模型选什么?中文BGE/M3E,通用text-embedding-3
搜索不准怎么办?换模型/增大召回top-k/混合检索/重排序
数据量1亿+怎么办?Milvus集群 + IVF索引 + 合理分区
如何提高搜索速度?减小ef/用GPU索引/缓存热门结果
如何保证搜索准确性?用Cross-Encoder重排序 / RRF融合

Released under the MIT License.