Milvus、Qdrant、Chroma 怎么选:一套可复现的比较方法
“哪个向量数据库最快”是一个容易提问、却很难诚实回答的问题。Milvus、Qdrant 和 Chroma 的产品边界并不完全相同:它们面向的数据规模、部署方式、扩展模型和运维要求都有差异。只比较一组 QPS,往往会把不同定位的产品拉进一场不公平的比赛。
本文不复用某次旧版本跑分,也不宣布一个永久冠军,而是给出一套可以在自己的硬件和数据上重复运行的比较方法。
先确定是不是在比较同一类问题
选型前先写清楚系统约束:
- 数据量是 10 万、1000 万还是 10 亿;
- 单机即可,还是需要水平扩展;
- 主要是离线写入,还是持续增量更新;
- 是否大量使用标量过滤;
- 能否接受服务端运维;
- 是否需要备份、监控、故障恢复和多租户;
- 目标召回率和 P95/P99 延迟是多少。
三者现在都覆盖不止一种部署方式,但产品边界仍不相同:Milvus 提供 Lite、Standalone 和 Distributed;Qdrant 提供 Local Mode、自托管服务和 Qdrant Cloud;Chroma 提供 Local、Single-Node、Distributed 和 Cloud。名称相似不代表扩展模型、运维能力或适用规模相同,最终应以各项目当前文档和自己的需求矩阵为准。
为什么旧跑分不能直接用于今天
Zilliz 公众号在 2024 年发布过一篇 Milvus、Qdrant、Chroma 的对比,使用当时的 Milvus 2.4.5、Qdrant 1.11.5 和 Chroma 0.5.8 开发版本。文章记录了测试机器、部署方式和 VectorDBBench 流程,这些方法有参考价值;但其性能结论受版本、参数和厂商来源影响,不应直接外推到当前版本。
复用旧文章时,最应该保留的是透明度:公开硬件、镜像版本、参数、超时、失败记录与资源监控。最不应该保留的是“某数据库永远最快”的结论。
公平比较的第一原则:先对齐召回率
近似向量索引通过牺牲部分召回率换取速度。一个系统使用激进参数取得更高 QPS,另一个系统保持更高召回率,两者的延迟无法直接比较。
正确流程是:
- 使用同一数据集与查询集;
- 计算精确近邻作为 Ground Truth;
- 调整每个数据库的索引与搜索参数;
- 让它们达到相近的
Recall@10; - 再比较延迟、吞吐和资源。
最终报告应使用“召回率—延迟曲线”,而不是只展示一个最好数字。
建议的工作负载矩阵
一轮完整测试至少包含以下组合:
| 维度 | 建议范围 | 目的 |
|---|---|---|
| 数据规模 | 100K、1M,条件允许再增加 10M | 观察增长趋势,而非单点结果 |
| 向量维度 | 384、768、1536 | 模拟轻量、本地与 API 模型 |
| 查询 TopK | 10、100 | 观察返回集合变大后的变化 |
| 并发 | 1、8、32、64 | 找到饱和点和尾延迟 |
| 过滤 | 无过滤、10%、1%、0.1% 选择性 | 验证标量过滤能力 |
| 写入 | 全量导入、持续增量写入 | 观察混合负载 |
| 缓存 | 冷启动、预热后 | 区分磁盘与缓存效果 |
数据集可以从 ANN Benchmarks 或 VectorDBBench 支持的数据中选择。不要用完全随机向量替代真实分布做最终结论;随机向量适合验证脚本,不适合评估索引行为。
测试环境必须记录什么
建议为每次运行保存一个清单。下面只是待填写的配置模板,不代表本文已经在这组硬件上执行过测试:
run_id: replace-with-actual-run-id
hardware:
cpu: "填写实际 CPU 型号与核心数"
memory_gib: "填写实际容量"
disk: "填写实际介质、文件系统与 IOPS"
dataset:
name: "填写数据集与版本"
dimension: "填写实际维度"
entities: "填写实际数量"
workload:
top_k: 10
concurrency: [1, 8, 32]
filter_selectivity: [none, 0.1, 0.01]
targets:
recall_at_10: 0.95
databases:
milvus:
version: "填写实际版本"
deployment: standalone
qdrant:
version: "填写实际版本"
chroma:
version: "填写实际版本"
还应记录内核、容器运行时、文件系统、客户端版本、网络拓扑、索引参数、复制因子和数据是否已加载到内存。
六类指标缺一不可
1. 检索质量
至少记录 Recall@10 和 Recall@100。如果包含过滤查询,还要验证 Ground Truth 是否应用了相同过滤条件。
2. 延迟与吞吐
记录 P50、P95、P99 和 QPS。平均延迟会隐藏尾部抖动,不适合作为唯一指标。
3. 写入与索引
分别记录数据导入、索引构建、可查询时间和增量写入吞吐。部分系统写入返回成功后仍在后台构建或刷新,必须明确“完成”的定义。
4. 资源使用
记录 CPU、内存、磁盘空间、磁盘 I/O 和网络。资源图需要与测试阶段对齐,不能只截一张峰值图。
5. 稳定性
连续运行混合读写,记录错误率、超时、延迟漂移和内存增长。短时间峰值跑分无法说明长期稳定性。
6. 运维成本
检查部署、升级、监控、备份、恢复、扩容和故障处理。对于小团队,少一次夜间人工恢复,可能比提升 10% QPS 更有价值。
使用 VectorDBBench 时要注意什么
VectorDBBench 可以统一驱动多种数据库,但“同一个测试工具”不自动代表“公平”。运行前仍要检查:
- 每个客户端使用的 API 与批大小;
- 默认索引是否一致;
- 是否达到相近召回率;
- 超时是否被修改;
- 一个测试结束后数据是否完全清理;
- 是否一次只运行一个数据库,避免资源干扰;
- 测试工具和数据库是否部署在同一台机器。
如果测试工具与数据库同机,客户端会占用 CPU 和内存;如果分机,则必须报告网络延迟。两种方式都可以,但不能混用。
不要只测试“纯向量搜索”
生产查询通常还包括标量过滤、更新和返回字段。例如电商检索可能要求:
向量相似 + category = "shoes" + in_stock = true + region = "EU"
过滤选择性会改变查询计划。应至少测试高选择性和低选择性两组条件,并检查不同数据分布下的尾延迟。
另一个常见遗漏是读写并发。知识库可能每天批量更新,商品搜索可能持续写入。纯读测试无法说明索引更新期间的服务质量。
如何阅读最终结果
最终结果不应是一张总排名,而应是一组按场景解释的决策矩阵:
- 本地原型:安装速度、API 易用性和低资源占用优先;
- 单机在线服务:尾延迟、过滤、持久化和监控优先;
- 大规模生产集群:水平扩展、容量管理、高可用和运维工具优先;
- 持续更新场景:写入、删除、索引维护与读写隔离优先。
某个系统在 100K 数据上表现最好,不代表它在 100M 数据、复杂过滤或多副本部署中仍然最好。报告必须明确测试没有覆盖的范围。
一份可信报告的验收清单
- 三个数据库使用相同硬件和独立运行环境;
- 版本、镜像、客户端与所有参数已公开;
- 先对齐召回率,再比较性能;
- 同时报告 P50、P95、P99 和错误率;
- 包含过滤、增量写入和恢复检查;
- 原始结果和脚本可下载;
- 失败测试与超时也被保留;
- 结论按场景表达,不宣布永久冠军;
- 如果作者与某个项目存在关联,明确披露。
向量数据库选型不是一次跑分,而是一项容量和风险决策。最有价值的“对比文章”,不是替读者宣布答案,而是让读者能在自己的约束下复现答案。