跳到主要内容

向量数据库选型实战

上个项目选型时,我在 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 一览表

特性ChromaFAISSMilvusWeaviatePineconeQdrant
类型嵌入式数据库向量索引库分布式数据库向量数据库托管云服务向量数据库
开发语言Python/RustC++Go/C++GoRust
开源✅ Apache 2.0✅ MIT✅ Apache 2.0✅ BSD-3❌ 商业✅ Apache 2.0
部署方式嵌入式/客户端纯内存自部署/云自部署/云仅云自部署/云
数据规模<100 万<1000 万亿级千万级千万级千万级
持久化❌(需额外处理)
分布式
标量过滤
混合检索
多租户有限
学习成本

2.2 各数据库详解

Chroma — 轻量级首选

适合场景:原型开发、小规模生产、个人项目
数据规模:&lt;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 — 性能怪兽

适合场景:大规模纯内存检索、研究实验
数据规模:&lt;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 GB15ms
FAISS (Flat)~10000/s~5000~1.5 GB0.5ms
FAISS (IVF)~10000/s~8000~1.2 GB0.3ms
Milvus (IVF_FLAT)~2000/s~3000~1.8 GB2ms
Weaviate~800/s~500~2.0 GB8ms
Qdrant~1500/s~2000~1.3 GB3ms

注意:这些数据会因硬件配置、索引参数、数据分布等因素变化,仅供参考。

四、选型决策矩阵

你的情况推荐方案理由
个人项目 / 学习Chroma零配置,pip install 即用
原型验证 / MVPChroma → 后续迁移 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 模型。

七、参考资料