Milvus 向量数据库实战:架构、索引调参与 RAG 应用解析
第一次专门去翻 Milvus 的源码级资料是因为一个知识库项目要把几十万条文本的 embedding 向量做成语义检索。当时第一反应是在已有数据库里加字段折腾了一周发现无论是 MySQL 的 LIKE 还是普通索引都没法处理“意思相近但字面不同”的查询后来才把目光放到专门做向量检索的开源项目上。Milvus 就是在那个阶段进入我视野的。这篇文章想把我对它技术架构的理解、实际部署和检索调参过程以及它在 RAG 场景里的生态配合尽量讲透。内容适合三类人正在评估向量数据库选型的技术负责人已经部署好 Milvus 但不太清楚索引参数怎么调的后端开发以及用 PyMilvus 写检索逻辑、经常被召回率问题困扰的算法工程师。先说个结论Milvus 比很多同类产品更适合生产环境不是因为它的查询延迟一定最低而是因为它在数据模型、索引管理、水平扩展和生态整合这些容易被忽略的地方做得比较完整。你真正跑起来之后会发现这些“看不见”的能力才是决定项目能不能长期维护的关键。1. 先想明白向量数据库在解决什么实际问题1.1 传统检索方案的瓶颈做语义检索的核心操作是“找到与给定向量最相似的 K 个向量”。这类需求在推荐、查重、多模态搜索、RAG 知识库这些场景里非常常见。但传统数据库体系里这个操作其实没有特别顺手的工具。如果你用 MySQL通常只能把向量转成二进制或 JSON 存起来然后全表扫描计算距离。数据量在十万级以下还能勉强接受上了百万级一次查询就算用上并行计算也可能要几百毫秒甚至几秒更不要说 CPU 直接被打满。PostgreSQL 有个 pgvector 插件能把向量存储和检索集成进去对于中小型项目是很实用的方案但它在索引并发、数据迁移、水平分片这些方面还是有天然短板数据量一旦到了千万级管理成本会明显上升。Elasticsearch 的 dense_vector 类型也可以做向量检索不过它的强项是全文检索和日志分析把大规模向量索引放在 Elasticsearch 里内存压力和调优复杂度都不小。这些方案的共同问题是可以“做出来”但很难“规模化”。向量检索本质上是一个高维度、高吞吐的近似搜索问题它需要的是为海量向量设计的索引结构和分布式执行能力而不是在原有数据结构上打补丁。1.2 Milvus 的核心定位Milvus 把自己定位成“云原生向量数据库”核心目标就是解决上面这个规模化问题。它做了三件我觉得很重要的事。第一数据模型上采用集合Collection、分区Partition、字段Field的概念使用过传统数据库的人上手很快不用为了向量检索去学一套完全陌生的查询语言。第二索引和存储分离底层数据可以放到对象存储里索引异步构建这意味着你可以先写数据后建索引索引也能单独重建而不影响原始数据。第三分布式架构拆出了多个协调组件和工作节点好消息是横向扩展时不用停服务。我后来在实际项目里最大的感受是Milvus 的设计始终把“生产可用”放在第一位。比如它的一致性级别、动态字段、标量过滤这些能力都是你真正做业务时会用到的东西。这些细节让 Milvus 不只是一个“向量匹配工具”而是一个完整的数据库。2. Milvus 技术架构到底是怎么组织的2.1 组件与分工Milvus 2.x 的架构延续了“存算分离”的思路整体上可以分成四层接入层、协调服务、工作节点和依赖的基础组件。接入层主要是 SDK 和负载均衡客户端通过 gRPC 连接 Milvus请求会被分发给协调服务。协调服务里有四个角色RootCoord 负责管理 DDL 请求和系统元数据DataCoord 管理数据段和数据写入通道QueryCoord 管理查询节点的负载和查询计划IndexCoord 管理索引构建任务。工作节点这边则有 Datanode、Querynode 和 Indexnode分别负责数据落盘、执行查询和构建索引。基础依赖方面Milvus 使用 etcd 保存元数据使用 MinIO 或 S3 兼容对象存储保存日志快照和索引文件在分布式模式下还会用到 Pulsar 或 Kafka 作为消息流中间件。很多初次接触 Milvus 的人会疑惑为什么一个数据库要依赖这么多外部组件其实这正是它实现高可用的基础元数据、数据流、存储互相独立任何一个组件出问题都不会让整个系统立刻不可用。我当时看这个架构时的感受是Milvus 的组件职责切分得很干净。比如索引构建被单独拆成 Indexnode这带来的直接好处是大批量数据写入时索引构建跑在独立节点上不会占掉查询节点的 CPU 资源查询延迟就不会被后台任务拖垮。这种“各司其职”的思路在数据量大起来之后优势特别明显。2.2 数据模型与一致性Milvus 的数据模型是集合—分区—段这种层级结构。一个集合可以理解成一张表集合里的字段支持主键、向量字段、标量字段、动态字段。分区是集合内的二级分片常用做法是按时间或业务线划分比如新闻库按月建分区查询时只扫目标分区性能提升很明显。段是底层数据组织单位数据写入时先落在内存里形成增长段flush 之后变成密封段然后进入对象存储。一致性级别是 Milvus 一个容易被忽略但很重要的设计。它提供了四种一致性级别强一致、有界一致性、会话一致性和最终一致性。强一致性保证读写完全同步但延迟高最终一致性性能最好但查询可能读不到刚写入的数据。RAG 这类场景通常选择会话一致性既能保证当前会话里的数据可见又不会把性能拖得很难看。这里有一个我实际踩过的坑向 Milvus 写入数据后立即查询如果默认的 consistency_level 是最终一致性可能拿不到刚写入的数据。建议在创建集合时显式设置 consistency_levelSession否则测试环境里会出现“数据明明写进去了但查不到”的诡异问题。2.3 索引从 FLAT 到 HNSW 再到 DiskANN向量索引是 Milvus 最核心的部分。Milvus 支持的索引类型很多但最常用的就是 FLAT、IVF 家族、HNSW 和 DiskANN 这几类。FLAT 是暴力检索不建索引直接全量计算距离适合数据量小且精度要求极高的场景。IVF 系列IVF_FLAT、IVF_SQ8、IVF_PQ先对向量做聚类查询时只搜索最近的几个聚类中心用“粗筛精排”的方式减少计算量IVF_PQ 还会对向量做乘积量化能大幅压缩内存占用。HNSW 是基于图的索引原理是构建多层跳表结构每层由近邻连接构成查询时从高层往低层搜索在召回率和速度上都比较均衡因此它是目前最通用的选择。DiskANN 则把索引放在磁盘上用 SSD 解决内存装不下的问题适合超大规模场景。索引类型检索方式优点缺点典型场景FLAT全量计算100% 召回、实现简单数据量大时极慢百万级以下小集合IVF_FLAT聚类精确计算比 FLAT 快得多召回率受 nprobe 影响中等规模、内存充足IVF_PQ聚类乘积量化内存大幅降低有精度损失大规模、内存受限HNSW图结构近似搜索速度快、召回率高内存占用高、建索引慢常见生产场景首选DiskANN磁盘索引突破内存限制依赖 SSD、延迟略高上亿级向量我当时对索引选型的思路很简单数据量在千万级以下优先用 HNSW超过千万级但内存还能扛住用 IVF_PQ如果连内存预算都不够再去研究 DiskANN。关于索引参数怎么调比如 HNSW 的 M 和 efConstruction后面章节会专门展开。3. 部署形态standalone 模式与分布式如何选3.1 standalone 模式的组成与边界Milvus 提供了两种部署模式standalone 模式和分布式集群模式。standalone 模式把协调服务和工作节点压缩到一个进程里再加上 etcd 和对象存储两个依赖组件整体上就是一个非常轻量的单机服务。这种模式特别适合什么场景呢第一开发测试环境你在本地打算快速验证 Milvus 的功能不想引入一套 K8s 集群。第二数据量在千万级以下的生产环境比如一个中等规模的知识库查询的 QPS 要求也不高standalone 部署完全够用。第三学习和实验场景想研究索引参数对召回率的影响用 standalone 模式迭代速度最快。standalone 模式有一个很大的优势部署简单。官方提供的 Docker Compose 配置直接拉起三个容器几分钟就能用。但它也有明确的边界单点故障问题。Milvus 进程挂了整个检索服务就中断了而且数据量跨过一定门槛后单机内存会成为 HNSW 索引的瓶颈。所以如果你想做高可用或者预估数据量会快速膨胀那从一开始还是规划集群模式比较靠谱。3.2 什么时候应该上分布式集群分布式集群模式是把各个协调组件和工作节点拆开部署可以独立扩容。在 Milvus 2.3 之后的版本里消息流中间件也支持了 Kafka和 Pulsar 二选一这让它在私有化部署时的兼容性更好了。K8s 环境下可以使用 Milvus Operator 来管理集群扩容缩容直接改副本数就行。什么情况需要从 standalone 切换到集群我的判断标准有三个一是查询 QPS 上去了单机承载不了需要扩 Querynode 节点二是数据量超过内存预算需要把索引分布到多台机器三是对可用性有硬性要求不能接受单点故障。不过我要提醒一点不要让“分布式”变成你的默认选择。分布式部署带来的好处是扩展性但代价是运维复杂度明显上升排查问题的链路也长很多。如果你的数据量目前就是百万级standalone 加一台好一点的机器比如 64GB 内存可以撑很久。我在几个项目里都是先 standalone 起步等监控指标确实逼近阈值了才逐步迁移到集群。提前规划好元数据、对象存储这些路径的统一后续迁移会顺畅很多。4. 从安装到第一个检索实操记录4.1 本地快速安装 Milvus先讲最常见的安装方式Docker Compose 启动 standalone 模式。官方提供了一个 docker-compose.yml内容本质上是启动三个服务etcd、MinIO 和 milvus。# 下载 Docker Compose 配置文件 wget https://github.com/milvus-io/milvus/releases/download/v2.3.3/milvus-standalone-docker-compose.yml -O docker-compose.yml # 启动 sudo docker-compose up -d # 查看状态 sudo docker-compose ps启动完成后Milvus 默认监听19530端口这是 gRPC 端口另外还有一个9091端口用于健康检查。验证服务是否正常可以执行curl http://127.0.0.1:9091/healthz如果返回包含OK的响应说明服务已经就绪。我第一次启动时在旧版本踩过一个坑Docker Compose 里的 MinIO 容器启动比 Milvus 慢导致 Milvus 启动失败。解法很简单重启 Milvus 容器或者在 Compose 文件里给 Milvus 服务加上depends_on的条件确保 MinIO 和 etcd 就绪后再启动。如果你的机器连 Docker 都不想装Milvus 官方还提供了一个轻量级本地模式 milvus-lite直接通过 Python 在进程内启动一个 Milvus 实例特别适合做自动化测试和快速原型验证。pip install pymilvus milvus-litefrom milvus import default_server default_server.start() from pymilvus import connections connections.connect(host127.0.0.1, portdefault_server.listen_port)不过 milvus-lite 毕竟是嵌入式实现别拿它跑大规模生产数据我的建议是日常脚本验证、CI 测试用 milvus-lite正式环境用 Docker Compose 的 standalone 模式。4.2 PyMilvus 建库建集合PyMilvus 是 Milvus 的 Python SDK目前主流版本是 2.3.x。连接逻辑很简单但建集合之前要先定义 Schema。Schema 决定了集合里有哪些字段这一步很像关系数据库的建表语句但多了一个向量字段。from pymilvus import connections, CollectionSchema, FieldSchema, DataType # 连接 Milvus connections.connect(host127.0.0.1, port19530) # 定义字段 fields [ FieldSchema(nameid, dtypeDataType.INT64, is_primaryTrue, auto_idFalse), FieldSchema(nametext, dtypeDataType.VARCHAR, max_length1024), FieldSchema(nameembedding, dtypeDataType.FLOAT_VECTOR, dim768), ] schema CollectionSchema(fields, descriptionRAG knowledge base)这里一个关键点是dim必须和你的 embedding 模型输出维度一致。比如 OpenAI 的text-embedding-3-large输出 3072 维BGE-large-zh 输出 1024 维写错维度的话插入数据时会被直接拒绝。如果你刚开始不确定用什么模型建议先用 768 维或 1024 维预留好空间后续换模型时可以重新建集合没必要过度纠结。创建集合时还有一个容易被忽略的参数是consistency_level。在生产环境我通常建议显式设置为Session这可以避免刚写入数据就查询不到的问题。from pymilvus import Collection collection Collection(nameknowledge_base, schemaschema, consistency_levelSession)4.3 数据写入与索引构建集合创建好之后插入数据前先确认一个概念Milvus 的插入是“先落内存后异步落盘”的。你调用 insert 时数据先进入增长段这时数据还没有固化到对象存储中。如果服务中途宕机这部分数据可能丢失。调用 flush 之后数据才会密封并写入存储。# 准备数据 ids [i for i in range(len(vectors))] data [ ids, texts, vectors ] # 插入数据 collection.insert(data) # 强制落盘 collection.flush()flush 之后要做的第一件事是创建索引。没有索引的集合可以查询但走的是暴力扫描数据一多延迟就扛不住。创建索引时关键参数有三个索引类型、度量方式、索引参数。index_params { index_type: HNSW, metric_type: COSINE, params: {M: 8, efConstruction: 256} } collection.create_index(field_nameembedding, index_paramsindex_params)创建完索引后还需要调用load()把索引加载到内存中。很多人第一次用 Milvus 时漏了这一步结果 search 报“collection not loaded”的错误。查询性能和索引是否加载到内存直接相关加载完成后集合才算进入可查询状态。4.4 相似度检索余弦值、内积、欧式距离怎么用索引创建好之后终于可以执行最核心的相似度检索了。这里就涉及到“Milvus 余弦值”的实际用法。在 Milvus 里你可以选择三种度量方式L2欧式距离、IP内积、COSINE余弦相似度。collection.load() query_vector [0.12, 0.34, ...] # 768 维的查询向量 results collection.search( data[query_vector], # 查询向量列表 anns_fieldembedding, # 在哪个向量字段上搜索 param{metric_type: COSINE, params: {ef: 64}}, # 度量方式和查询参数 limit10, # 返回 top-K 结果 output_fields[id, text] # 返回哪些标量字段 ) for result in results[0]: print(fid{result.id}, distance{result.distance}, text{result.entity.get(text)})查询结果里返回的distance字段对应的就是该度量方式下的相似度得分。如果用的是 COSINE分数越接近 1 表示语义越相似。IP 是向量内积数值越大越相似但前提是向量最好做过归一化否则维度越高的向量内积天然越大结果会失真。L2 是几何距离数值越小越相似适合希望保留真实距离信息的情况。这里有一个重要的概念区分余弦相似度和内积的关系。如果你把所有向量都做了 L2 归一化那么内积就等于余弦相似度。很多 Embedding 模型尤其是 OpenAI 的接口默认输出的向量就是归一化之后的这时 IP 和 COSINE 的结果是等价的。但如果向量没有归一化你用 IP 和 COSINE 检索出来的排序结果会不一样的。所以我的习惯是在使用 IP 之前先确认 embedding 是否已做归一化不确定就用 COSINE语义上更直观出错概率也更小。5. 检索调参与常见参数深度说明5.1 度量方式的选择逻辑三种度量方式的选择不是一个“差不多就行”的决定它会直接影响召回效果和业务逻辑的解读。L2欧式距离适合向量各维度绝对值有意义的场景比如坐标点、物理特征。返回的距离值越小越好因为是距离不是相似度。IP内积适合已归一化向量计算速度快语义上相当于余弦相似度。但注意 IP 的得分范围很大容易受维度膨胀影响不建议在未归一化向量上使用。COSINE余弦相似度只关注方向、不关注模长语义相似度最直觉的选择。RAG 知识库检索里我基本固定用它。在同一个集合里度量方式在创建索引时就得定好。如果建索引用的 index params 里是metric_type: COSINE搜索时 param 里也要传COSINE不一致的话 Milvus 会直接报错。这种“前后一致”的要求看起来是约束其实也是一个安全设计避免你查询时的距离语义和索引时不一致导致结果混乱。5.2 HNSW 与 IVF 的核心查询参数HNSW 是生产环境最常用的索引它有两个核心参数影响查询效果M和efConstruction建索引时决定以及ef查询时决定。M 是每个节点的最大连接数。M 越大图越稠密召回率越高但对应的索引内存占用和构建时间也越高。efConstruction 是建索引时动态候选列表的大小越大代表构建图时搜索的候选节点越多图的质量越高召回率越好。查询时的 ef 参数控制要遍历多少候选节点ef 越大召回率越高但延迟也越高。我自己给出的起始参数是 M16efConstruction200查询时 ef64。这是准确性和资源消耗的平衡点。如果业务对召回率要求很高可以把 ef 调到 128 甚至 256代价是查询延迟从几毫秒上升到十几毫秒。注意 ef 太大会导致检索时间线性上升但回报会逐渐递减。IVF 家族的参数逻辑不同。建索引时的nlist是聚类中心个数nlist 越大聚类越细召回率越高查询时的nprobe是搜索的聚类中心数量nprobe 越大检索的区间越多召回率越高但延迟也越高。我一般是 nlist1024查询时 nprobe16 起步再根据召回率逐步调。另外提供一个实践经验索引参数不是一次定死的。你可以先建索引然后调整查询时的ef或nprobe不用重新建索引就能测试不同召回率下的效果。只有改建索引参数比如 M、nlist、efConstruction时才需要重建索引。所以建议现场尽量多用查询参数去适配别一上来就反复重建索引。5.3 一致性级别带来的差别一致性级别影响的是“读到的数据是不是最新写入的”。四个级别从强到弱依次是强一致、有界一致、会话一致、最终一致。用得最多的其实是会话一致和有界一致。强一致每次读都要求所有副本同步完成写入延迟和查询延迟都高一般不用在检索场景。会话一致同一客户端会话内写入后读取能看到最新数据跨会话不保证。适合 RAG 知识库逐条写入、立即验证的场景。有界一致允许一个时间窗口内的数据滞后窗口可配置性能和一致性比较平衡。最终一致所有副本最终达到一致查询可能读不到刚写入的数据但吞吐最高。我的建议是如果不是对数据一致性没有概念的场景不要默认使用最终一致。特别是在写入后马上查询的调试场景里Session 一致性会让你少踩很多坑。而批量离线导入的流程里通常会先 flush 再查询这时用有界或最终一致影响不大。6. Milvus 的生态位在 RAG 和大模型应用中的最佳实践6.1 Embedding 数据源向量数据库本身不产生向量向量来自 Embedding 模型。Milvus 能成为 RAG 事实标准之一很大程度得益于它和主流 Embedding 体系的兼容性。不管是 OpenAI 的 embedding 接口、Cohere 的嵌入模型、还是开源的中文模型 BGE、M3E、text2vecMilvus 都只是把它当成一维数组。在中文知识库场景里我常用的两个模型组合是 BGE-large-zh 和 M3E。BGE 对中文长文本的语义理解比较扎实输出 1024 维M3E 更轻量在短文本分类和检索上表现不错。选择模型之前先确认输出的向量是否归一化。如果模型没有归一化检索时建议用 COSINE而不是 IP否则相似度分数会失去可解释性。此外还要注意一个细节向量维度不同占用的内存差异很大。同样是 1000 万条数据768 维的 float32 向量占内存约 28GB1536 维就要 56GB这还不算索引的开销。如果遇到内存紧张的问题可以考虑使用 32 维到 256 维的小模型或者用 Milvus 的二进制向量BINARY_VECTOR、稀疏向量功能这些是官方持续迭代的方向。6.2 RAG 链路中 Milvus 的位置RAG 的典型流程是文档切分、Embedding 化、向量入库、用户查询时再做相似度检索、把检索到的文本片段交给大模型组织答案。这个链路里Milvus 就是那个“记忆体”。大模型记不住全部私域知识Milvus 负责在需要的时候把最相关的语料捞出来。用 LangChain 和 Milvus 对接可以说是最简单直接的方式。LangChain 的 Milvus 向量库封装已经比较成熟几行代码就能完成写入和检索from langchain_community.vectorstores import Milvus from langchain_community.embeddings import HuggingFaceBgeEmbeddings embedding_model HuggingFaceBgeEmbeddings(model_nameBAAI/bge-large-zh-v1.5) vector_store Milvus.from_documents( documentstext_chunks, embeddingembedding_model, collection_nameknowledge_base, connection_args{host: 127.0.0.1, port: 19530}, ) # 查询 docs vector_store.similarity_search(query如何配置日志系统, k5)这里有两个容易被忽视的点。第一文本切片策略对检索效果的影响比向量数据库本身还大。切片太小语义不完整太大则包含太多噪声。我习惯先按段落切再控制在 300~500 字之间。第二LangChain 初始化 Milvus 时默认会用一个文本字段存原文但如果你还想存其他元数据如来源页码、文档标题需要提前做好字段映射否则后面追踪答案来源时会很痛苦。另外在实际的 RAG 工程里我通常会做“召回后重排”的二级过滤。Milvus 先返回 top50 的候选文档再用交叉编码器模型精排取 top5这样可以显著提升大模型生成答案的正确率。Milvus 只负责第一轮粗召回这个定位要明确不建议把精度压力全部押在向量检索上。6.3 周边生态工具Milvus 的生态在开源向量数据库里算是相当完整的。值得提的工具主要有这么几个AttuMilvus 官方桌面端 GUI类似 MySQL 的 Navicat可以浏览集合、执行查询、监控数据分布排障的时候非常方便。Milvus CLI命令行工具适合脚本化操作。Milvus Backup官方数据备份工具支持集合级别的备份和恢复生产环境建议定时跑。BirdwatcherMilvus 诊断工具可以分析 etcd 中的元数据状态定位段分布不均、索引状态异常之类的问题。Milvus OperatorKubernetes 环境下的部署和运维工具管理集群生命周期、扩缩容和升级。框架层面除了 LangChainMilvus 还和 LlamaIndex、Haystack 等 RAG 框架有官方集成。这些集成让 Milvus 能直接作为 VectorStore 被上层框架调用。有一点需要清醒认识Milvus 不是万能的搜索引擎。它不擅长全文检索和复杂的 SQL 关联查询。如果业务既要关键词匹配又要语义检索通常的做法是 Milvus 与 Elasticsearch 双写ES 管关键词过滤和布尔查询Milvus 管向量召回两个结果做融合排序。7. 实战中的问题排查与避坑清单7.1 常见问题速查表现象可能原因解决方案查询报 collection not loaded没有调用 load()执行 collection.load()写入后立刻查询无结果一致性级别过低设置 consistency_levelSession插入时报 dim 不匹配向量维度和 Schema 定义不一致检查模型输出维度和 Schema 的 dim查询延迟突然暴涨查询时 ef/nprobe 过大或内存不足降低 ef/nprobe增加内存或扩容建索引后查询结果仍不准索引参数不合适或数据未 flush调整 M/efConstruction先 flush 再建索引删除数据后空间不释放未做 compaction执行 compact 操作清理旧段CPU 长时间高负载数据增长段过多或未建索引flush 数据创建合理索引多个集合查询 QPS 上不去单机资源瓶颈考虑扩容 Querynode 或上集群7.2 几个特别容易踩的细节第一个细节是删除数据的后续处理。Milvus 的删除是逻辑删除数据不是立刻从磁盘里消失。如果你频繁执行删除和更新操作会发现索引文件越来越臃肿、查询越来越慢这时候需要手动触发 compaction 来合并数据段。否则就算删掉了大量数据磁盘空间和索引内存也不会自动释放。第二个细节是批量插入时的 flush 时机。频繁调用 flush 会触发段密封在小批量写入场景下会制造大量碎片文件增加后续合并的成本。正确做法是批量插入后统一 flush或者依赖 Milvus 的自动 flush 机制。频繁写入的流式场景里我一般不会显式 flush而是让数据积累到一定规模再落盘。第三个细节是段和索引的内存估算。很多人以为建了 HNSW 索引后内存只需要装向量数据就够了。实际上 HNSW 的图结构会带来额外内存开销。实测下来M16 时 HNSW 索引的内存占用大约是原始向量的 1.2~1.5 倍。如果机器内存预算卡得紧可以考虑 IVF_PQ它能通过量化把内存压到原始数据的四分之一甚至更低代价是召回精度略有下降。第四个细节是动态字段的影响。Milvus 支持动态字段写入时可以带上 Schema 之外的字段查询时能做过滤。但动态字段如果滥用会产生很多不可预期的元数据影响写入和查询性能。我的建议是业务核心字段写死在 Schema 里动态字段只用于临时标记或调试不要拿它做主查询路径。最后说点实在的跑过几个 Milvus 项目之后我最大的体会是向量数据库的上手门槛并不高真正花时间的其实是索引参数、数据治理和跟业务语义的配合。每次换 Embedding 模型都要重新评估召回效果每次数据规模上一个量级都要重新审视内存和索引策略这些事情没有捷径只能一个个用例去测。从我个人的实践经验来说Milvus 是一个下限很高、上限也足够高的选择。如果只是做原型验证半小时就能跑通搜索如果认真像对待自己的数据库一样去运维它它也能支撑起比较大规模的真实业务。希望这篇文章能帮你在使用 Milvus 的时候少走几段弯路。