跳到主要内容

Milvus、Qdrant、Chroma 怎么选:一套可复现的比较方法

· 阅读需 7 分钟
XlongLab
AI 产品、技术与工程实践

“哪个向量数据库最快”是一个容易提问、却很难诚实回答的问题。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,另一个系统保持更高召回率,两者的延迟无法直接比较。

正确流程是:

  1. 使用同一数据集与查询集;
  2. 计算精确近邻作为 Ground Truth;
  3. 调整每个数据库的索引与搜索参数;
  4. 让它们达到相近的 Recall@10
  5. 再比较延迟、吞吐和资源。

最终报告应使用“召回率—延迟曲线”,而不是只展示一个最好数字。

建议的工作负载矩阵

一轮完整测试至少包含以下组合:

维度建议范围目的
数据规模100K、1M,条件允许再增加 10M观察增长趋势,而非单点结果
向量维度384、768、1536模拟轻量、本地与 API 模型
查询 TopK10、100观察返回集合变大后的变化
并发1、8、32、64找到饱和点和尾延迟
过滤无过滤、10%、1%、0.1% 选择性验证标量过滤能力
写入全量导入、持续增量写入观察混合负载
缓存冷启动、预热后区分磁盘与缓存效果

数据集可以从 ANN Benchmarks 或 VectorDBBench 支持的数据中选择。不要用完全随机向量替代真实分布做最终结论;随机向量适合验证脚本,不适合评估索引行为。

测试环境必须记录什么

建议为每次运行保存一个清单。下面只是待填写的配置模板,不代表本文已经在这组硬件上执行过测试:

YAML
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 和内存;如果分机,则必须报告网络延迟。两种方式都可以,但不能混用。

不要只测试“纯向量搜索”

生产查询通常还包括标量过滤、更新和返回字段。例如电商检索可能要求:

Text
向量相似 + category = "shoes" + in_stock = true + region = "EU"

过滤选择性会改变查询计划。应至少测试高选择性和低选择性两组条件,并检查不同数据分布下的尾延迟。

另一个常见遗漏是读写并发。知识库可能每天批量更新,商品搜索可能持续写入。纯读测试无法说明索引更新期间的服务质量。

如何阅读最终结果

最终结果不应是一张总排名,而应是一组按场景解释的决策矩阵:

  • 本地原型:安装速度、API 易用性和低资源占用优先;
  • 单机在线服务:尾延迟、过滤、持久化和监控优先;
  • 大规模生产集群:水平扩展、容量管理、高可用和运维工具优先;
  • 持续更新场景:写入、删除、索引维护与读写隔离优先。

某个系统在 100K 数据上表现最好,不代表它在 100M 数据、复杂过滤或多副本部署中仍然最好。报告必须明确测试没有覆盖的范围。

一份可信报告的验收清单

  • 三个数据库使用相同硬件和独立运行环境;
  • 版本、镜像、客户端与所有参数已公开;
  • 先对齐召回率,再比较性能;
  • 同时报告 P50、P95、P99 和错误率;
  • 包含过滤、增量写入和恢复检查;
  • 原始结果和脚本可下载;
  • 失败测试与超时也被保留;
  • 结论按场景表达,不宣布永久冠军;
  • 如果作者与某个项目存在关联,明确披露。

向量数据库选型不是一次跑分,而是一项容量和风险决策。最有价值的“对比文章”,不是替读者宣布答案,而是让读者能在自己的约束下复现答案。

参考资料