向量数据库深入指南
“向量数据库是 RAG 的记忆中枢——它存储了知识的向量表示,让 AI 能够快速检索。”
一、为什么需要向量数据库?
传统数据库存储的是结构化数据(表、行列),查询用的是精确匹配(WHERE id = 1)。
向量数据库存储的是高维向量(通常 768~4096 维),查询的是近似匹配——找到”最相似”的向量。
1 2 3 4 5 6 7 8
| 传统查询: "找出 id = 123 的用户" → 精确匹配 ✓
向量查询: "找出与 '机器学习' 最相关的 10 篇文章" → 近似匹配 ↓ 语义相似 ≠ 字面相同 "深度学习" 和 "机器学习" 在向量空间中很接近
|
在大模型和 RAG 场景中,向量数据库负责高效存储和检索知识的向量表示。
二、向量相似度度量
2.1 余弦相似度(Cosine Similarity)
衡量两个向量方向的相似度,取值范围 [-1, 1]。
1 2 3 4 5 6
| cosine_similarity(A, B) = (A · B) / (||A|| × ||B||)
向量 A = [1, 0] 向量 B = [1, 0] → cosine = 1.0(完全相同方向) 向量 C = [0, 1] → cosine = 0.0(垂直) 向量 D = [-1, 0] → cosine = -1.0(完全相反)
|
适用场景:文本 embedding(如 sentence-transformers),因为 embedding 模型通常对向量做 L2 归一化。
2.2 点积(Dot Product)
直接计算向量内积,取值范围无界限。
1
| dot_product(A, B) = Σ(A_i × B_i)
|
注意:如果向量未归一化,点积会偏向长的向量( magnitude 大的向量相似度分数更高)。通常需要先做归一化。
2.3 欧几里得距离(L2 Distance)
衡量向量间的直线距离。
1
| L2_distance(A, B) = √(Σ(A_i - B_i)²)
|
适用场景:图像向量、语音嵌入。当”距离”有物理含义时(如人脸识别),L2 通常更好。
2.4 如何选择?
| 度量方式 |
适用场景 |
注意事项 |
| 余弦相似度 |
文本、推荐系统 |
已归一化的向量 |
| 点积 |
未归一化的向量、需要放大差异 |
先归一化 |
| L2 距离 |
图像、人脸、语音 |
对 magnitude 敏感 |
三、向量索引算法
3.1 暴力搜索的问题
假设有 100 万个向量,每个向量 768 维。找 top-1 最相似需要计算 100 万次点积——这在实时场景中不可接受。
1 2 3 4 5
| 暴力搜索复杂度:O(N × D) N = 向量数量 D = 向量维度
100万向量 × 768维 = 7.68亿次乘法
|
向量索引的目标:用大幅降低计算量的方式,找到”近似”最近邻——这就是 ANN(Approximate Nearest Neighbor,近似最近邻)。
3.2 IVF(Inverted File Index)
核心思想:将向量空间划分成多个聚类( Voronoi cells),检索时只搜索最近的几个聚类,而不是全部。
1 2 3 4 5 6 7 8 9 10 11
| 训练过程: 1. 用 K-Means 将所有向量分成 N 个聚类(如 1024 个) 2. 每个聚类有一个中心点(centroid) 3. 每个向量归属于离它最近的中心点对应的聚类
检索过程: 1. 计算 query 和所有中心点的距离,找到最近的 K 个聚类 2. 只在这 K 个聚类的向量中搜索 (K=1 就是只搜一个聚类,K=N 就是暴力搜索)
时间复杂度:O(N × D / K) ← 只搜 K 个聚类
|
IVF 的参数:
nlist:聚类数量(太多→聚类不准,太少→搜索范围大)
nprobe:搜索时查几个聚类(越大越准,越慢)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import faiss
d = 768 nlist = 1024
quantizer = faiss.IndexFlatIP(d) index = faiss.IndexIVFFlat(quantizer, d, nlist)
index.train(train_vectors) index.add(vectors)
index.nprobe = 16 D, I = index.search(query_vectors, k=10)
|
3.3 HNSW(Hierarchical Navigable Small World)
核心思想:构建一个多层级图结构,从高层到底层逐步逼近目标。
1 2 3 4 5 6
| HNSW 结构: Layer 3: ○───────○───────○ ← 最稀疏,只有关键连接 /│ │ │\ Layer 2: ○─○──○───○─○──○───○─○ ← 中层 /││\ │ /││\ │ /││\ Layer 1: ○─○─○─○─○─○─○─○─○─○─○─○─○ ← 最密集
|
检索过程(从顶层开始):
1 2 3 4 5
| 1. 从 Layer 3 的某个入口点开始 2. 找到当前节点的最近邻(贪心搜索) 3. 如果找不到更近的节点,下降到 Layer 2 4. 重复,直到 Layer 0 5. 在 Layer 0 做穷举搜索找到最近邻
|
为什么快?
- 高层节点稀疏,可以用大步长快速定位”大致区域”
- 底层节点密集,确保找到真正的最近邻
- 图结构保证了搜索路径的连通性
HNSW 的参数:
M:每个节点的最大连接数(越大越准,越耗内存)
efConstruction:构建时的搜索范围(越大越准,越慢)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import faiss
d = 768 M = 32 efConstruction = 200
index = faiss.IndexHNSWFlat(d, M) index.hnsw.efConstruction = efConstruction index.add(vectors)
index.hnsw.efSearch = 200 D, I = index.search(query_vectors, k=10)
|
3.4 PQ(Product Quantization)
核心思想:将高维向量”压缩”成短编码,检索时在压缩空间中进行距离计算。
1 2 3 4 5 6 7 8 9
| 原始向量:768 维 × 4 字节 = 3072 字节
PQ 压缩: 1. 将向量切成 M 段(如 M=8,每段 96 维) 2. 每段独立做 K-Means 聚类(如 256 个簇) 3. 用簇中心 ID(1 字节)代替原始向量 4. 最终编码:M × 1 字节 = 8 字节
压缩率:3072 / 8 = 384 倍!
|
检索过程:
1 2 3
| 1. query 也切成 M 段 2. 计算 query 每段和对应簇中心的距离(查表) 3. 累加各段距离,找到最近的编码
|
PQ 的问题是有损压缩,精度不如原始向量。适合海量数据+内存受限的场景。
1 2 3 4 5 6 7 8 9 10 11 12
| import faiss
d = 768 m = 96 nbits = 8
index = faiss.IndexPQ(d, m, nbits) index.train(train_vectors) index.add(vectors)
D, I = index.search(query_vectors, k=10)
|
3.5 组合索引:IVF + HNSW + PQ
实际生产中,通常组合使用多种算法:
1 2 3 4 5 6 7 8 9 10 11 12
| d = 768 nlist = 4096 M = 32
quantizer = faiss.IndexHNSWFlat(d, M) index = faiss.IndexIVFPQ(quantizer, d, nlist, 96, 8) index.train(vectors) index.add(vectors)
index.nprobe = 64 D, I = index.search(query_vectors, k=10)
|
3.6 索引算法对比
| 算法 |
精度 |
速度 |
内存 |
适用场景 |
| 暴力搜索(Flat) |
100% |
最慢 |
最大 |
小数据集、精确匹配 |
| IVF |
~95-99% |
快 |
中 |
中等规模、平衡型 |
| HNSW |
~95-99% |
最快 |
大 |
小-中等规模、高QPS |
| PQ |
~80-95% |
最快 |
最小 |
海量数据、内存受限 |
| IVF+HNSW |
~95-99% |
快 |
中 |
生产环境推荐 |
四、主流向量数据库对比
4.1 Milvus
特点:开源、功能最全、性能最强,适合大规模生产部署。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from pymilvus import connections, Collection
connections.connect("default", host="localhost", port="19530")
collection = Collection("articles") collection.load()
search_params = {"metric_type": "IP", "params": {"nprobe": 16}} results = collection.search( data=[query_vector], anns_field="embedding", param=search_params, limit=10, expr=None )
|
适用场景:大规模(1B+ 向量)、需要精细控制、 Kubernetes 部署。
4.2 Pinecone
特点:全托管云服务、零运维、简单易用,适合快速启动。
1 2 3 4 5 6 7 8 9 10 11 12
| import pinecone
pinecone.init(api_key="...", environment="us-west1") index = pinecone.Index("articles")
results = index.query( vector=query_vector, top_k=10, include_metadata=True )
|
适用场景:不想运维、快速原型、中小规模(免费层够用)。
4.3 Weaviate
特点:内置向量化和 GraphQL API,支持多种 embedding 模型,适合语义搜索 + 结构化查询结合的场景。
1 2 3 4 5 6 7 8 9 10
| import weaviate
client = weaviate.Client("http://localhost:8080")
result = client.query.get( "Article", ["title", "content"] ).with_near_vector({ "vector": query_vector }).with_limit(10).do()
|
4.4 Chroma
特点:轻量级、纯 Python、易嵌入现有应用,适合本地开发和测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import chromadb
client = chromadb.PersistentClient(path="./chroma_db") collection = client.get_or_create_collection("articles")
collection.add( ids=["1", "2"], embeddings=[vec1, vec2], metadatas=[{"title": "A"}, {"title": "B"}], documents=["doc1", "doc2"] )
results = collection.query( query_embeddings=[query_vector], n_results=10 )
|
4.5 对比总结
| 特性 |
Milvus |
Pinecone |
Weaviate |
Chroma |
| 部署方式 |
自托管/云 |
全托管云 |
自托管/云 |
本地/嵌入式 |
| 规模 |
1B+ |
100M+ |
10M+ |
1M 以下 |
| 性能 |
极高 |
高 |
高 |
中 |
| 运维复杂度 |
高 |
低 |
中 |
极低 |
| 费用 |
开源免费 |
按量付费 |
开源免费 |
免费 |
| 额外功能 |
混合搜索、分片 |
自动扩缩容 |
GraphQL |
轻量集成 |
五、向量数据库的核心操作
5.1 CRUD 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| index.insert([ {"id": "1", "vector": vec1, "metadata": {"text": "文档内容"}}, {"id": "2", "vector": vec2, "metadata": {"text": "另一篇"}}, ])
results = index.query(vector=query_vec, top_k=10)
index.update([{"id": "1", "vector": new_vec, "metadata": {"text": "更新后"}}])
index.delete(ids=["1"])
|
向量数据库通常支持先过滤再检索:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| results = index.query( vector=query_vector, top_k=10, filter={"category": {"$eq": "AI"}} )
filter = { "$and": [ {"category": {"$eq": "AI"}}, {"publish_year": {"$gte": 2023}}, {"author": {"$in": ["张三", "李四"]}} ] }
|
注意:过滤操作通常无法利用向量索引(需要遍历),大量过滤会导致性能下降。最佳实践是先用向量检索快速召回候选,再用过滤筛选。
5.3 分区和分片
当数据量非常大时,需要分区(partitioning)或分片(sharding):
1 2 3 4 5 6
| collection.create_partition("partition_2024") collection.insert([...], partition_name="partition_2024")
collection.search(..., partitions=["partition_2024"])
|
1 2 3 4 5 6 7 8 9 10
|
etcd: address: etcd:2379 minio: address: minio:9000
shard_nums = 4 collection = Collection("articles", shards_num=shard_nums)
|
六、Embedding 模型选择
6.1 常用文本 Embedding 模型
| 模型 |
维度 |
适用语言 |
特点 |
| text-embedding-ada-002 |
1536 |
多语言 |
OpenAI 官方,稳定可靠 |
| text-embedding-3-large |
3072 |
多语言 |
OpenAI 最新,更强 |
| bge-large-zh |
1024 |
中英 |
国产开源,中文优秀 |
| m3e-large |
1024 |
中英 |
国产开源,轻量快速 |
| e5-base-v2 |
768 |
多语言 |
微软,适合检索 |
6.2 Embedding 质量评估
使用 MTEB(Benchmark for Massive Text Embedding)评估:
1 2 3 4 5 6 7 8 9 10
| from mteb import MTEB from sentence_transformers import SentenceTransformer
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
evaluation = MTEB(task_names=["Retrival"]) results = evaluation.run(model, output_folder=f"results/{model_name}")
print(results)
|
6.3 重编码(Re-embedding)问题
向量数据库中存储的 embedding 是静态的,但:
解决方案:
1 2 3 4 5 6 7 8 9 10
| def reindex_documents(collection, documents, embedder): new_embeddings = embedder.embed(documents) collection.delete(all=True) collection.add([ {"id": str(i), "vector": vec, "text": doc} for i, (vec, doc) in enumerate(zip(new_embeddings, documents)) ])
|
七、实战经验
7.1 选型建议
1 2 3 4 5 6 7
| 数据量 < 100万 → Chroma(最简单) 100万 ~ 1亿 → Pinecone / Weaviate(云服务或自托管) 1亿以上 → Milvus(必须分布式)
需要混合查询(向量+结构化)→ Weaviate / Milvus 追求最低成本 → 自托管(Milvus/Weaviate) 最快上线 → Pinecone
|
7.2 性能调优
1 2 3 4 5 6 7 8 9 10 11 12 13
| search_params = { "metric_type": "IP", "params": { "nprobe": 16, "ef": 200 } }
|
7.3 常见问题
Q:向量数据库和普通数据库有什么区别?
1 2
| 普通数据库:精确匹配 WHERE id = 1 向量数据库:近似匹配 "找出和这个向量最相似的 10 个"
|
Q:Embedding 怎么存?
1 2 3
| 通常存在向量数据库中,但也可以: - PostgreSQL + pgvector(不想多引入一个数据库时) - Elasticsearch(已有 ES 集群时)
|
Q:向量数据库能保证精确搜索吗?
1 2
| 不能保证。ANN 算法是"近似"最近邻,不是精确最近邻。 如果需要 100% 精确,用 Flat 索引(小数据集)或接受近似结果。
|
八、总结
向量数据库是 RAG 和语义搜索的核心基础设施:
- 度量选择:余弦相似度最常用(文本),L2 适合图像/语音
- 索引算法:HNSW(快速高精度)、IVF(省内存)、PQ(海量压缩)
- 选型:小规模用 Chroma,中等用 Pinecone/Weaviate,超大规模用 Milvus
- 过滤:metadata filtering 很有用,但要注意性能影响
- Embedding 模型:中文推荐 bge-large-zh,多语言用 text-embedding-3-large
相关文章: