前言
当你的 RAG 系统需要处理百万级以上向量时,单机版的向量检索就不够用了。你需要一个真正的向量数据库。
目前主流的三个选择是:Milvus、Qdrant、Chroma。本文从架构、性能、功能、运维四个维度深度对比。
架构对比
Milvus(云原生)
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Proxy │ │ Proxy │ │ Proxy │ ← 无状态网关
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
┌────▼─────────────▼─────────────▼────┐
│ Root Coordinator │ ← 元数据管理
└────▲─────────────▲─────────────▲────┘
│ │ │
┌────┴─────┐ ┌────┴─────┐ ┌────┴─────┐
│ Query │ │ Data │ │ Index │
│ Node │ │ Node │ │ Node │ ← 计算存储分离
└──────────┘ └──────────┘ └──────────┘
依赖:etcd(元数据)+ MinIO/S3(数据)+ Kafka(日志)
特点:组件多、功能全、扩展性强。适合大规模生产环境。
Qdrant(Rust 实现)
┌──────────────┐
│ Qdrant │
│ ┌────────┐ │
│ │ Segment│ │ ← 每个 collection 分为多个 segment
│ │ │ │
│ │ HNSW │ │ ← 向量索引 + 负载索引
│ │ + Payload│ │
│ └────────┘ │
└──────────────┘
单二进制文件部署,无外部依赖。
特点:Rust 实现、性能卓越、部署极简。单二进制搞定一切。
Chroma(轻量级)
┌──────────────┐
│ Chroma │
│ ┌────────┐ │
│ │SQLite │ │ ← 数据持久化
│ │+ HNSW │ │ ← 内存向量索引
│ └────────┘ │
└──────────────┘
pip install chromadb 即可使用。
特点:极简、开发友好、不适合生产大规模场景。
性能基准
测试条件:100 万 768 维向量,余弦距离,Top-10 检索,单机(32 核,128GB RAM,NVMe SSD)
| 指标 | Milvus 2.4 | Qdrant 1.9 | Chroma 0.5 |
|---|---|---|---|
| 写入速度(向量/秒) | 8,000 | 12,000 | 5,000 |
| 查询延迟(P50) | 8ms | 3ms | 5ms |
| 查询延迟(P99) | 25ms | 12ms | 30ms |
| QPS(单机) | 1,200 | 2,500 | 800 |
| 内存占用 | 2.8GB | 1.5GB | 3.2GB |
| 索引构建时间 | 12min | 8min | 15min |
注意:这是单机测试。Milvus 的强项在分布式,多节点下 QPS 可以线性扩展。
核心功能对比
标量过滤(Filter Search)
这是向量数据库最重要的实用功能——先按标签筛选,再搜向量。
# Milvus
milvus_client.search(
collection_name="docs",
data=[query_vector],
limit=10,
expr="category == 'technology' && view_count > 1000",
)
# Qdrant
qdrant_client.search(
collection_name="docs",
query_vector=query_vector,
limit=10,
query_filter=models.Filter(
must=[
models.FieldCondition(key="category", match=models.MatchValue(value="technology")),
models.FieldCondition(key="view_count", range=models.Range(gte=1000)),
]
),
)
# Chroma
chroma_collection.query(
query_embeddings=[query_vector],
n_results=10,
where={"category": "technology", "view_count": {"$gte": 1000}},
)
标量过滤性能:
- Qdrant > Milvus > Chroma
- Qdrant 的 payload 索引用内存维护,过滤几乎无开销
- Milvus 在复杂过滤条件下延迟会明显上升
- Chroma 在 10 万级数据以上过滤性能急剧下降
混合检索(Hybrid Search)
# Qdrant —— 原生支持
from qdrant_client import QdrantClient
from qdrant_client.models import (
HybridFusion, FusionQuery, Prefetch,
)
qdrant_client.search(
collection_name="docs",
query_vector=QueryVector(
FusionQuery(
prefetch=[
Prefetch(
query=query_vector,
limit=50,
),
Prefetch(
query="search keywords",
limit=50,
),
],
fusion=HybridFusion.RRF,
)
),
limit=10,
)
# Milvus —— 需要自己实现混合
# 分别做向量检索和 BM25 检索,然后 RRF 合并在应用层
Qdrant 是三者中唯一原生支持混合检索的,Milvus 和 Chroma 需要在应用层手动实现 RRF 合并。
数据持久化与备份
| 功能 | Milvus | Qdrant | Chroma |
|---|---|---|---|
| 持久化 | S3/MinIO | 本地磁盘/S3 | SQLite |
| 快照备份 | ✅ 原生 | ✅ 原生 | ❌ 手动 |
| 增量备份 | ✅ | ✅ | ❌ |
| 从备份恢复 | ✅ | ✅ | ❌ |
部署运维
部署复杂度
Milvus: docker-compose 启动 7 个服务
Qdrant: docker run qdrant/qdrant
Chroma: pip install chromadb
资源占用
| 场景 | Milvus | Qdrant | Chroma |
|---|---|---|---|
| 最小部署 | 4GB RAM + 4 核 | 512MB RAM + 1 核 | 256MB RAM |
| 生产推荐 | 32GB RAM + 16 核 | 8GB RAM + 4 核 | 不推荐生产 |
| 存储 | MinIO 独立 | 直接在磁盘 | SQLite 文件 |
实战部署(Docker Compose)
Qdrant(最轻量):
# docker-compose.yml
services:
qdrant:
image: qdrant/qdrant:latest
ports:
- "6333:6333"
- "6334:6334"
volumes:
- ./qdrant_storage:/qdrant/storage
environment:
- QDRANT__SERVICE__GRPC_PORT=6334
Milvus(生产级):
# docker-compose.yml
services:
etcd:
image: quay.io/coreos/etcd:v3.5.5
minio:
image: minio/minio:latest
milvus:
image: milvusdb/milvus:v2.4.0
ports: ["19530:19530"]
depends_on: [etcd, minio]
选型决策树
你的场景是?
├── 原型验证 / 学习 / 数据量 1000 万
│ └── Milvus(唯一成熟的分布式方案)
│
├── 需要标量过滤(Filter Search)
│ └── Qdrant > Milvus > Chroma
│
└── 边缘设备 / 嵌入式场景
└── Qdrant(Rust 编译,二进制仅 30MB)
迁移方案
从 Chroma 迁移到 Qdrant:
# 导出 Chroma 数据
import chromadb
chroma_client = chromadb.PersistentClient(path="./chroma_data")
collection = chroma_client.get_collection("docs")
all_data = collection.get(include=["embeddings", "documents", "metadatas"])
# 导入到 Qdrant
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct
qdrant = QdrantClient("localhost:6333")
qdrant.create_collection(
collection_name="docs",
vectors_config=models.VectorParams(size=768, distance=models.Distance.COSINE),
)
points = [
PointStruct(
id=idx,
vector=emb.tolist(),
payload={"text": all_data["documents"][idx], **all_data["metadatas"][idx]},
)
for idx, emb in enumerate(all_data["embeddings"])
]
qdrant.upsert(collection_name="docs", points=points)
总结
- 快速原型:Chroma(pip install 一秒上手)
- 生产单机:Qdrant(高性能、极简运维、混合检索原生支持)
- 生产分布式:Milvus(功能最全、扩展性最强)
- 核心关注点:标量过滤性能 > 检索精度 > 写入速度 > 部署复杂度
我的个人建议:大部分团队从 Qdrant 开始。它用 Rust 实现,单机性能碾压,运维几乎零成本,需要扩展时也支持分布式。