向量数据库选型:Milvus和Qdrant我都上了生产,说点实话

292 阅读5分钟

今年我们公司在搞知识库问答系统,向量数据库是绕不开的一环。网上关于Milvus和Qdrant的对比文章很多,但大多是"部署个demo跑一下"就下结论,真上过生产、扛过真实流量的不多。

这两个我都部署过,Milvus跑了大半年,Qdrant也跑了几个月。不是要分个高下,而是想说说在真实生产环境里,它们各自的脾气。

先说结论:如果你的场景是"纯向量检索、数据量大、要横向扩展",Milvus合适;如果你的场景是"向量+元数据过滤、部署要简单、团队没专门运维",Qdrant会让你省心很多。 我们最终是混合用的,两个都在线上。


我们的场景

知识库问答,数据是公司内部的文档、政策、FAQ,总量大概1200万条,每天新增几万条。

两个核心需求:

  1. 用户提问,先用向量检索召回相关文档片段(TopK=20)
  2. 按业务线、文档类型、时间范围做过滤,再在过滤结果里做向量检索

第二个需求是后加的,也正是它改变了我对这两个数据库的看法。


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万条数据,我做了个简单压测:

指标MilvusQdrant
纯向量检索 P998ms12ms
带过滤检索 P9915ms9ms
部署依赖组件etcd+MinIO+Pulsar无(单容器)
内存占用约4G约2.5G

有意思的是:纯向量检索Milvus快,但带上过滤之后Qdrant反而快。 原因是Qdrant的过滤是在向量检索过程中一起做的,而Milvus的标量过滤是检索后的二次处理,多了一道工序。

这也印证了选型的关键:没有绝对的优劣,只有场景的匹配。


我的选型建议

用了一年多,我的判断标准简化成了三条:

  1. 数据量在百万级以内,选Qdrant,部署简单、过滤好用,完全够
  2. 数据量千万级以上,且以纯向量检索为主,选Milvus,扩展性和索引能力更强
  3. 有强过滤需求(多条件组合),优先考虑Qdrant,哪怕数据量大点,过滤性能的差距是实打实的

另外多说一句:别一上来就上向量数据库。 如果你的数据量只有几万条,用个普通数据库加个Embedding列,暴力扫描都够了。向量数据库是有成本的,别为了技术选型而选型。


结尾

向量数据库选型这件事,我的体会是:demo跑得好不代表生产用得好,生产环境的运维复杂度、过滤场景、数据规模,才是真正决定选型的因素。

我们的现状是Milvus和Qdrant并存,各自服务合适的场景。虽然运维上多了一套东西,但比硬用一个适配所有场景舒服得多。

如果这篇对你有用,点个关注。后面我会写我们知识库的RAG链路是怎么搭的——向量检索只是其中一环,切块、召回、重排这些环节的坑更多。