今年我们公司在搞知识库问答系统,向量数据库是绕不开的一环。网上关于Milvus和Qdrant的对比文章很多,但大多是"部署个demo跑一下"就下结论,真上过生产、扛过真实流量的不多。
这两个我都部署过,Milvus跑了大半年,Qdrant也跑了几个月。不是要分个高下,而是想说说在真实生产环境里,它们各自的脾气。
先说结论:如果你的场景是"纯向量检索、数据量大、要横向扩展",Milvus合适;如果你的场景是"向量+元数据过滤、部署要简单、团队没专门运维",Qdrant会让你省心很多。 我们最终是混合用的,两个都在线上。
我们的场景
知识库问答,数据是公司内部的文档、政策、FAQ,总量大概1200万条,每天新增几万条。
两个核心需求:
- 用户提问,先用向量检索召回相关文档片段(TopK=20)
- 按业务线、文档类型、时间范围做过滤,再在过滤结果里做向量检索
第二个需求是后加的,也正是它改变了我对这两个数据库的看法。
Milvus:能力全面,但运维是真重
Milvus我们先用的,当时看中它两点:检索性能强,生态完整(和Spring AI有官方集成)。
架构上是存算分离,组件多:etcd(元数据)、MinIO(对象存储)、Pulsar(消息队列)。生产环境用Docker Compose起了一套,光是这几个依赖组件就要单独运维。
优点:
- 性能确实猛。 1200万条数据,HNSW索引,单查询延迟稳定在10ms以内。高峰期每秒几百个查询毫无压力
- 索引类型丰富。 HNSW、IVF、DISKANN都有,数据量大了可以换DISKANN做磁盘索引,内存吃紧的场景很有用
- 水平扩展方便。 数据量上来了,加节点就行,存算分离的设计天生适合扩
踩过的坑:
- 运维复杂度远超预期。 etcd、MinIO、Pulsar三个组件,任何一个出问题都会牵连Milvus。我们生产上Pulsar挂过一次,整个集群检索直接不可用,排查了快两个小时才定位到是它
- 小数据量场景杀鸡用牛刀。 你如果只有几十万条数据,Milvus这套架构完全是大炮打蚊子,资源浪费严重
- 文档过滤功能要自己拼。 我们的第二个需求(先过滤再检索),Milvus虽然支持标量过滤,但和向量检索是分开配置的,表达式写起来啰嗦,调试费劲
Qdrant:简单直接,过滤是杀手锏
后来我们在一个新项目(客户侧的文档问答)里用了Qdrant,原因就一个:部署简单,只要一个容器。
单机部署,一个Docker容器搞定,没有一堆依赖组件。这点对中小团队太友好了。
优点:
- 部署和运维成本低一个量级。 一个容器跑起来就是全部,升级也简单
- Payload过滤是真的好用。 Qdrant的核心设计就是"向量+元数据"一体,检索的时候直接带过滤条件,一个API搞定。我们的"按业务线+文档类型过滤"需求,在Qdrant里就是加一个Filter参数的事
// Qdrant:向量检索+标量过滤,一个调用搞定
Filter filter = Filter.newBuilder()
.addMust(FieldCondition.newBuilder()
.setKey("biz_line")
.setMatch(Match.newBuilder().setKeyword("售后").build())
.build())
.addMust(FieldCondition.newBuilder()
.setKey("doc_type")
.setMatch(Match.newBuilder().setKeyword("政策").build())
.build())
.build();
SearchPoints search = SearchPoints.newBuilder()
.setCollectionName("knowledge")
.addAllVector(queryVector)
.setFilter(filter)
.setLimit(20)
.setWithPayload(WithPayloadSelector.newBuilder().setEnable(true).build())
.build();
- REST API直接可用。 不写代码都能先curl试一把,调试体验比Milvus的gRPC舒服多了
踩过的坑:
- 索引类型单一。 主要就是HNSW,百万级数据够用,但到了千万级、上亿级,调优空间不如Milvus大
- 单节点有瓶颈。 虽然支持集群,但架构上不如Milvus的存算分离扩展得那么干净。数据量真上亿了,还是Milvus更从容
- Java SDK文档一般。 能用,但有些API设计绕,遇到问题查文档经常要翻半天
数据说话
同样的机器配置(8核16G),同样的1200万条数据,我做了个简单压测:
| 指标 | Milvus | Qdrant |
|---|---|---|
| 纯向量检索 P99 | 8ms | 12ms |
| 带过滤检索 P99 | 15ms | 9ms |
| 部署依赖组件 | etcd+MinIO+Pulsar | 无(单容器) |
| 内存占用 | 约4G | 约2.5G |
有意思的是:纯向量检索Milvus快,但带上过滤之后Qdrant反而快。 原因是Qdrant的过滤是在向量检索过程中一起做的,而Milvus的标量过滤是检索后的二次处理,多了一道工序。
这也印证了选型的关键:没有绝对的优劣,只有场景的匹配。
我的选型建议
用了一年多,我的判断标准简化成了三条:
- 数据量在百万级以内,选Qdrant,部署简单、过滤好用,完全够
- 数据量千万级以上,且以纯向量检索为主,选Milvus,扩展性和索引能力更强
- 有强过滤需求(多条件组合),优先考虑Qdrant,哪怕数据量大点,过滤性能的差距是实打实的
另外多说一句:别一上来就上向量数据库。 如果你的数据量只有几万条,用个普通数据库加个Embedding列,暴力扫描都够了。向量数据库是有成本的,别为了技术选型而选型。
结尾
向量数据库选型这件事,我的体会是:demo跑得好不代表生产用得好,生产环境的运维复杂度、过滤场景、数据规模,才是真正决定选型的因素。
我们的现状是Milvus和Qdrant并存,各自服务合适的场景。虽然运维上多了一套东西,但比硬用一个适配所有场景舒服得多。
如果这篇对你有用,点个关注。后面我会写我们知识库的RAG链路是怎么搭的——向量检索只是其中一环,切块、召回、重排这些环节的坑更多。