向量数据库选型实战
上个项目选型时,我在 Chroma、FAISS、Milvus 之间纠结了三天。Chroma 说轻量好用,FAISS 说性能无敌,Milvus 说生产级部署。最后选了 Chroma,结果数据量上来后查询变慢,又花了一周迁移到 Milvus。这次踩坑让我明白:向量数据库选型不是选"最好的",而是选"最适合当前阶段的"。
一、为什么需要向量数据库
传统数据库(MySQL、PostgreSQL)擅长精确匹配和范围查询,但对"语义相似性搜索"无能为力。
传统数据库查询:
SELECT * FROM docs WHERE title LIKE '%Python%' -- 精确匹配
向量数据库查询:
"Python 异步编程怎么学" → [0.12, -0.34, 0.56, ...] -- 语义向量
→ 找到最相似的 Top-K 向量 -- 近似最近邻搜索(ANN)
→ 返回对应的文档
向量数据库的核心能力:高效存储和检索高维向量。
二、主流向量数据库对比
2.1 一览表
| 特性 | Chroma | FAISS | Milvus | Weaviate | Pinecone | Qdrant |
|---|---|---|---|---|---|---|
| 类型 | 嵌入式数据库 | 向量索引库 | 分布式数据库 | 向量数据库 | 托管云服务 | 向量数据库 |
| 开发语言 | Python/Rust | C++ | Go/C++ | Go | — | Rust |
| 开源 | ✅ Apache 2.0 | ✅ MIT | ✅ Apache 2.0 | ✅ BSD-3 | ❌ 商业 | ✅ Apache 2.0 |
| 部署方式 | 嵌入式/客户端 | 纯内存 | 自部署/云 | 自部署/云 | 仅云 | 自部署/云 |
| 数据规模 | <100 万 | <1000 万 | 亿级 | 千万级 | 千万级 | 千万级 |
| 持久化 | ✅ | ❌(需额外处理) | ✅ | ✅ | ✅ | ✅ |
| 分布式 | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| 标量过滤 | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| 混合检索 | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ |
| 多租户 | 有限 | ❌ | ✅ | ✅ | ✅ | ✅ |
| 学习成本 | 低 | 中 | 高 | 中 | 低 | 中 |
2.2 各数据库详解
Chroma — 轻量级首选
适合场景:原型开发、小规模生产、个人项目
数据规模:<10 万文档
部署方式:pip install chromadb,一行代码启动
import chromadb
# 创建客户端(内存模式)
client = chromadb.Client()
# 或持久化存储
client = chromadb.PersistentClient(path="./chroma_db")
# 创建集合
collection = client.create_collection(
name="docs",
metadata={"hnsw:space": "cosine"},
)
# 添加文档
collection.add(
documents=["RAG 是什么", "向量数据库怎么选"],
ids=["doc1", "doc2"],
metadatas=[{"source": "wiki"}, {"source": "blog"}],
)
# 查询
results = collection.query(
query_texts=["检索增强生成"],
n_results=5,
where={"source": "wiki"}, # 标量过滤
)
优点:上手极快,Python 原生,开发体验好 缺点:不支持分布式,数据量大了性能下降明显
FAISS — 性能怪兽
适合场景:大规模纯内存检索、研究实验
数据规模:<1000 万向量
部署方式:pip install faiss-cpu/gpu
注意:不是完整数据库,没有持久化和元数据管理
import faiss
import numpy as np
# 创建索引
dimension = 1536 # OpenAI embedding 维度
index = faiss.IndexFlatIP(dimension) # 内积相似度
# 也可以用 IVF 索引加速(适合大规模数据)
nlist = 100 # 聚类中心数
quantizer = faiss.IndexFlatIP(dimension)
index = faiss.IndexIVFFlat(quantizer, dimension, nlist, faiss.METRIC_INNER_PRODUCT)
# 添加向量
vectors = np.random.random((10000, dimension)).astype('float32')
index.train(vectors) # IVF 需要训练
index.add(vectors)
# 查询
query = np.random.random((1, dimension)).astype('float32')
distances, indices = index.search(query, k=5)
优点:查询速度极快,支持 GPU 加速,Facebook 出品 缺点:没有持久化、没有元数据管理、需要自己包装成服务
Milvus — 生产级首选
适合场景:企业级生产环境、大规模数据
数据规模:亿级向量
部署方式:Docker Compose / Kubernetes / Zilliz Cloud
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
# 连接 Milvus
connections.connect("default", host="localhost", port="19530")
# 定义 Schema
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536),
FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=2000),
FieldSchema(name="source", dtype=DataType.VARCHAR, max_length=100),
]
schema = CollectionSchema(fields, description="Document embeddings")
# 创建集合
collection = Collection("docs", schema)
# 创建索引(IVF_SQ8 适合大规模数据)
index_params = {
"metric_type": "COSINE",
"index_type": "IVF_FLAT",
"params": {"nlist": 1024},
}
collection.create_index("embedding", index_params)
# 插入数据
collection.insert([embeddings, texts, sources])
# 查询
collection.load()
results = collection.search(
data=[query_embedding],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"nprobe": 16}},
limit=5,
output_fields=["text", "source"],
)
优点:分布式、高可用、支持多种索引类型、社区活跃 缺点:部署复杂(依赖 etcd、MinIO、Pulsar),学习成本高
Weaviate — GraphQL 友好
适合场景:需要 GraphQL API、多模态检索、快速原型
数据规模:千万级
部署方式:Docker / Weaviate Cloud
import weaviate
client = weaviate.connect_to_local() # 或 connect_to_weaviate_cloud
# 创建集合
client.collections.create(
name="Document",
vectorizer_config=weaviate.classes.config.Configure.Vectorizer.text2vec_openai(),
properties=[
weaviate.classes.config.Property(name="text", data_type=weaviate.classes.config.DataType.TEXT),
weaviate.classes.config.Property(name="source", data_type=weaviate.classes.config.DataType.TEXT),
],
)
# 自动向量化并插入
docs = client.collections.get("Document")
docs.data.insert({"text": "RAG 系统架构设计", "source": "wiki"})
# 混合查询(向量 + 关键词)
results = docs.query.hybrid(
query="检索增强生成",
alpha=0.75, # 0=纯关键词, 1=纯向量
limit=5,
)
优点:自带向量化、GraphQL API、多模态支持 缺点:社区相对小、自部署配置项多
Pinecone — 全托管省心
适合场景:不想运维、快速上线、预算充足
数据规模:千万级
部署方式:仅云服务(SaaS)
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key="YOUR_API_KEY")
# 创建索引
pc.create_index(
name="docs",
dimension=1536,
metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1"),
)
# 揌入数据
index = pc.Index("docs")
index.upsert(vectors=[
{"id": "doc1", "values": embedding, "metadata": {"source": "wiki"}},
])
# 查询
results = index.query(
vector=query_embedding,
top_k=5,
filter={"source": {"$eq": "wiki"}},
)
优点:零运维、自动扩缩容、开箱即用 缺点:数据锁定云端、成本随规模增长、无法定制底层
Qdrant — Rust 性能新秀
适合场景:追求性能、需要高级过滤、Rust 生态
数据规模:千万级
部署方式:Docker / Qdrant Cloud
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
client = QdrantClient(host="localhost", port=6333)
# 创建集合
client.create_collection(
collection_name="docs",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)
# 插入数据
client.upsert(
collection_name="docs",
points=[PointStruct(id=1, vector=embedding, payload={"text": "RAG", "source": "wiki"})],
)
# 带过滤的查询
results = client.query_points(
collection_name="docs",
query=query_embedding,
limit=5,
query_filter={"must": [{"key": "source", "match": {"value": "wiki"}}]},
)
优点:Rust 实现性能好、丰富的过滤条件、Payload 灵活 缺点:社区较新、中文文档少
三、性能基准测试
在 10 万条 1536 维向量上的测试结果:
| 数据库 | 插入速度 | 查询 QPS | 内存占用 | 延迟 (P99) |
|---|---|---|---|---|
| Chroma | ~500/s | ~200 | ~1.2 GB | 15ms |
| FAISS (Flat) | ~10000/s | ~5000 | ~1.5 GB | 0.5ms |
| FAISS (IVF) | ~10000/s | ~8000 | ~1.2 GB | 0.3ms |
| Milvus (IVF_FLAT) | ~2000/s | ~3000 | ~1.8 GB | 2ms |
| Weaviate | ~800/s | ~500 | ~2.0 GB | 8ms |
| Qdrant | ~1500/s | ~2000 | ~1.3 GB | 3ms |
注意:这些数据会因硬件配置、索引参数、数据分布等因素变化,仅供参考。
四、选型决策矩阵
| 你的情况 | 推荐方案 | 理由 |
|---|---|---|
| 个人项目 / 学习 | Chroma | 零配置,pip install 即用 |
| 原型验证 / MVP | Chroma → 后续迁移 Milvus | 快速启动,架构预留迁移能力 |
| 中小规模生产(<100 万) | Qdrant 或 Weaviate | 性能好,部署简单 |
| 大规模生产(>100 万) | Milvus | 分布式、高可用、社区大 |
| 不想运维 | Pinecone 或 Zilliz Cloud | 全托管,按量付费 |
| 纯研究 / GPU 加速 | FAISS | 性能极致,但需要自己包装 |
| 多模态检索 | Weaviate | 原生支持图片、文本混合检索 |
五、踩坑记录
坑 1:FAISS 没有持久化,重启全丢
问题:用 FAISS 存了 50 万条向量,测试环境重启后数据全没了。
解决:FAISS 只是索引库,不是数据库。需要自己实现持久化:
# 保存索引
faiss.write_index(index, "index.faiss")
# 加载索引
index = faiss.read_index("index.faiss")
但元数据(文档内容、来源等)需要另外存(如 SQLite)。如果需要完整的数据库能力,还是用 Chroma/Milvus。
坑 2:Chroma 在 Docker 中性能差
问题:Chroma 在 Docker 容器内运行,查询延迟从本地的 5ms 飙升到 50ms。
解决:Chroma 的默认配置不适合容器化。需要调整:
- 设置
chromadb.config.Settings(anonymized_telemetry=False) - 增大 SQLite 缓存大小
- 考虑用 Chroma 的客户端-服务器模式,把数据库放在容器外
坑 3:Milvus 部署太复杂
问题:Milvus 依赖 etcd(元数据)、MinIO(对象存储)、Pulsar(消息队列),Docker Compose 一拉就是 7-8 个容器。
解决:
- 开发阶段用 Milvus Lite(嵌入式版本,
pip install pymilvus) - 生产环境用 Zilliz Cloud(全托管),或用 Milvus 的 Docker Compose 一键部署脚本
- 小规模场景考虑 Qdrant(单容器部署)
坑 4:Pinecone 成本随数据量爆炸
问题:10 万条数据每月 $70,100 万条直接 $700,成本线性增长。
解决:
- 评估是否真的需要全托管,自建 Milvus 成本可能更低
- 用 Pinecone 的 Starter 计划(免费额度)做原型验证
- 设置数据生命周期策略,淘汰过期数据
坑 5:向量维度不一致导致查询报错
问题:用 text-embedding-3-small(1536 维)存数据,换成 bge-small-zh(512 维)查询,直接报维度不匹配。
解决:向量数据库的集合创建时就锁定了维度。如果要换 Embedding 模型,需要重新创建集合、重新向量化所有数据。所以选型时就要确定好 Embedding 模型。
七、参考资料
- Milvus 官方文档:https://milvus.io/docs
- Chroma 官方文档:https://docs.trychroma.com/
- FAISS 官方文档:https://faiss.ai/
- Qdrant 官方文档:https://qdrant.tech/documentation/
- Weaviate 官方文档:https://weaviate.io/developers/weaviate
- Pinecone 学习中心:https://www.pinecone.io/learn/