1.1 向量数据库与 Milvus 的定位:从 Embedding 检索到相似度搜索
我第一次接触向量数据库是在做一个图像搜索的小项目。当时用户上传一张图,系统要在几十万张商品图里找出最相似的那几张。用传统的 SQL LIKE 查询根本做不了这种事,因为图片经过神经网络处理后,变成了一串浮点数,比如 [0.12, -0.87, 0.33, ...],长度可能是 512、768 甚至 1536 维。这串数字就是 Embedding,它把一张图片、一段文本、一条用户行为记录压缩成了一个高维空间里的点。相似的东西,它们的点在空间里靠得近。检索的本质,就是在这个高维空间里找“离我最近的那些点”。
这件事说起来简单,做起来没那么轻松。几百万个 768 维向量放在一起,暴力算距离是 O(n),每次查询都要遍历全部数据。数据量一上来,延迟就跟不上了。向量数据库解决的就是这个问题:它把向量存下来,建索引,用近似最近邻(ANN)算法把搜索复杂度从线性降到接近对数级别。Milvus 是我用得比较多的一款,它的定位很清晰——专门为大规模向量相似度搜索设计,支持十亿级别向量的毫秒级查询,同时还能做标量字段过滤。你可以在一次搜索里既要求“向量相似”,又要求“价格低于 100 且库存大于 0”,这种混合查询能力在实际业务里非常关键。
相似度搜索只是入口。我用 Milvus 做过语义搜索、推荐召回、去重、聚类预筛选,甚至拿它做过异常检测——把正常样本的向量存进去,新来一个数据就查它和正常样本的距离,距离太远就报警。向量数据库不是替代传统数据库的,它更像一个专门处理“语义距离”的引擎,和关系型数据库、搜索引擎配合使用。Milvus 在这类场景里承担的角色,就是那个又快又稳的“相似度检索层”。
1.2 Milvus 核心概念与数据模型:Collection、Partition、Segment、Schema、Field
刚上手 Milvus 的时候,我被一堆概念绕晕过。后来用多了才发现,把它的数据模型和关系型数据库做个类比,理解起来会轻松很多。Collection 相当于一张表,Partition 相当于表里的逻辑分区,Field 就是列。Schema 定义了这张表的结构——有哪些字段、每个字段是什么类型、哪个字段是向量字段、向量维度是多少。我在建 Collection 之前一定会先想清楚 Schema,因为字段类型和向量维度一旦确定,后面改起来很麻烦。
Partition 是我特别喜欢的一个设计。举个例子,我做多租户的文档检索,每个租户的数据放在同一个 Collection 里,但用租户 ID 做 Partition。查询的时候指定 Partition key,Milvus 就只在那一个分区里搜索,数据量和延迟都降下来了。Partition 不是必须的,但它对隔离数据和加速查询很有帮助。一个 Collection 默认有一个 _default 分区,你也可以手动创建更多。
Segment 是 Milvus 内部存储和索引的基本单位,用户平时不太需要直接操作它。当你往 Collection 里插入数据,Milvus 会把数据先写到内存里的 growing segment,等积累到一定量再 flush 成 sealed segment 并落盘。索引是建在 segment 上的,查询的时候会并行搜索多个 segment 再合并结果。理解这一层有助于排查性能问题——比如为什么刚插入的数据搜不到、为什么索引还没生效,往往都跟 segment 的状态有关。Field 则是 Schema 的组成部分,标量字段支持 INT、VARCHAR、BOOL、FLOAT 等类型,向量字段需要指定维度和数据类型(FLOAT_VECTOR 或 BINARY_VECTOR)。
1.3 Milvus 系统架构与关键组件:Proxy、Coordinator、Query/Data/Index Node、存储与消息队列
Milvus 的架构设计是存算分离加组件化的。我一开始觉得组件太多,后来自己部署了几次,画了架构图,才慢慢理清楚。最外层是 Proxy,它是客户端请求的入口,负责接收 SDK 发来的请求、做权限校验、路由转发和结果聚合。你用 Python 或 Java SDK 连 Milvus 的时候,连的就是 Proxy。Proxy 本身是无状态的,可以水平扩展。
Coordinator 是“大脑”的角色,分成 Root Coordinator、Query Coordinator、Data Coordinator 和 Index Coordinator。Root 管全局的元数据和 DDL 操作,Query 管查询节点的负载均衡,Data 管数据插入和持久化,Index 管索引构建任务的调度。这些 Coordinator 通过消息队列(Pulsar 或 Kafka)和存储层(S3、MinIO 等对象存储)协调工作。Node 是“手脚”:Query Node 负责执行向量搜索和标量过滤,Data Node 负责把日志数据转成持久化的 segment,Index Node 负责构建索引。每一类 Node 都可以独立扩缩容,这是 Milvus 能支撑大规模场景的关键。
存储层分三块:元数据存储(etcd)、对象存储(MinIO/S3)和消息队列(Pulsar/Kafka)。etcd 存的是 Collection Schema、索引信息、节点状态这些元数据。对象存储存的是实际的向量数据、索引文件和日志快照。消息队列负责组件之间的异步通信和日志流。我第一次看这套架构觉得复杂,但实际用起来,Standalone 模式会把所有组件塞进一个进程,对外只暴露一个端口,开发阶段完全不用操心这些细节。等业务长大了再切换到分布式集群,架构不用改,这也是 Milvus 的一个优势。
1.4 环境准备与安装部署:Milvus Lite、Standalone、Docker Compose、集群模式
装 Milvus 有好几种方式,我根据不同场景都用过。如果你只是想快速试一下,Milvus Lite 是最省事的,它就是一个 Python 包,pip install pymilvus 之后在代码里指定一个本地文件路径就能跑起来,不需要 Docker,不需要服务端。我拿它做过原型验证和 Jupyter Notebook 里的教学演示,非常方便。不过 Lite 的功能是子集,数据量大了、需要多用户或者要上生产就不能用了。
本地开发和测试,我一般用 Docker Compose 起 Standalone 模式。官网提供了 compose 文件,docker compose up -d 一条命令就能拉起 Milvus、etcd 和 MinIO。它会暴露 19530 端口给 SDK 连接,9091 端口给监控指标。Standalone 支持大部分核心功能,索引类型也齐全,数据存在本地磁盘上。我的笔记本上跑个几十万向量做功能验证完全够用。
生产环境就得上集群模式了,用 Helm Chart 部署到 Kubernetes。Milvus Operator 或者 Milvus Helm Chart 都能帮你管理这套分布式组件,支持自动扩缩容和滚动升级。我做过一次从 Standalone 迁移到 K8s 集群的操作,数据可以通过备份恢复工具迁移,Schema 基本不用改。选哪种部署方式,关键看你的数据规模、可用性要求和运维能力。小团队用 Standalone 跑生产也没什么问题,只要做好备份和监控。
1.5 快速上手:连接 Milvus、创建集合、插入向量、建立索引、加载与搜索
我第一次跑通 Milvus 全流程大概花了半小时。从 pip install pymilvus 开始,连上本地的 Standalone 实例,然后按“建 Collection → 插数据 → 建索引 → 加载 → 搜索”这个顺序走一遍。代码不复杂,但有几个坑我当时踩过,值得提一下。连接的时候用 MilvusClient 比较简洁,client = MilvusClient(uri="http://localhost:19530") 就行。
创建 Collection 的时候,我习惯用 create_collection 的快速模式,直接指定维度、主键字段和向量字段。比如 client.create_collection(collection_name="demo", dimension=768)。如果要加标量字段和自定义 Schema,就用 create_schema 显式定义。插入数据用 client.insert,传一个字典列表,每个字典包含主键、向量和标量字段的值。这里注意主键如果开 auto_id 就不用自己传,否则要保证唯一。
建索引是容易被忽略的一步。Milvus 在没建索引的情况下也能搜索,但走的是暴力扫描,慢。我一般插入数据后马上建索引,用 client.create_index 指定索引类型和度量方式。建完索引还要 load_collection 把数据加载到内存,然后才能搜。搜索用 client.search,传入查询向量、要返回的字段和 top_k。整个流程走完,你会看到返回的 ID 和距离值。距离越小或越大(取决于度量方式),表示越相似。跑通这个最小闭环之后,后面所有的复杂功能都是在这个基础上叠加的。
1.6 索引与度量选择:FLAT、IVF、HNSW、DiskANN、GPU 索引与 L2/IP/COSINE
选索引这件事,我一开始是凭感觉的,后来认真做了几组对比实验,才明白不同索引的取舍在哪里。FLAT 是暴力搜索,不建任何索引结构,召回率 100%,但速度最慢,百万级以上就不太现实了。它适合做基准测试或者数据量很小的情况。IVF 系列(IVF_FLAT、IVF_SQ8、IVF_PQ)通过聚类把向量空间划分成多个桶,搜索时只查最近几个桶,速度快了,召回率会有些损失。nlist 控制桶的数量,nprobe 控制搜索几个桶,这两个参数需要根据数据分布调。
HNSW 是我在中小规模场景里用得最多的。它构建一个多层图结构,查询时从上层往下层逐层逼近,查询速度快、召回率高,而且对参数不那么敏感。代价是内存占用比 IVF 大,构建索引也慢一些。DiskANN 是近几年比较受关注的,它把索引放在磁盘上,内存只存少量缓存,适合数据量特别大、内存预算有限的场景。GPU 索引(GPU_IVF_FLAT、GPU_CAGRA)适合有 GPU 资源、追求极致吞吐的场景,我做过一次 GPU 索引的压测,QPS 比 CPU 版本高了将近十倍。
度量方式必须和你的 Embedding 模型匹配,这一点我踩过坑。L2 是欧氏距离,适合图像特征这类没有归一化的向量。IP 是内积,适合已经归一化过的向量,计算结果和余弦相似度等价。COSINE 是余弦相似度,Milvus 会在内部对向量做归一化再算内积。如果你用 OpenAI 或 BGE 这类模型生成的 Embedding,它们通常已经归一化了,用 COSINE 或 IP 都行。我见过有人用 L2 去搜归一化向量,结果排序看起来“怪怪的”,其实换成 COSINE 就正常了。索引和度量的组合要在建 Collection 时就定好,中途改要重建索引。
1.7 数据管理与运维基础:分区、分片、一致性、TTL、批量导入、监控与备份
日常运维 Milvus 的时候,我关注的东西其实就那么几样:数据怎么组织、写入一致性怎么样、旧数据怎么清理、监控看什么、备份怎么做。Partition 前面提过,是用得最多的数据组织手段。分片(Shard)是另一个维度,它决定数据在多个 Query Node 之间怎么分布,建 Collection 时通过 shards_num 指定。分片多了写入吞吐高,但查询时要合并更多结果,需要权衡。
一致性级别是我觉得最需要理解的一个概念。Milvus 提供 Strong、Bounded、Eventually、Session 四种。Strong 保证你能读到最新写入的数据,但延迟会高一些。Eventually 不保证立即可见,但吞吐最好。我默认用 Bounded,它在大多数场景下能兼顾一致性和性能。如果你插入数据后马上搜索却搜不到,很可能就是一致性级别设成了 Eventually,等一会儿或者换成 Strong 就好了。
TTL 用来设置数据的过期时间,到期自动删除,适合日志类、会话类场景。批量导入我推荐用 Bulk Insert 接口,把数据写成 JSON 或 Parquet 文件放到对象存储,然后调用接口导入,比一条条 insert 快很多。监控方面,Milvus 暴露了 Prometheus 格式的指标,配上 Grafana 面板能看 QPS、延迟、内存、segment 数量等。备份用官方提供的 milvus-backup 工具,支持全量和增量备份到对象存储。我一般会在每次大版本升级前做一次全量备份,心里踏实。
1.8 生态集成与入门实战:Attu、LangChain、LlamaIndex、RAG 最小检索示例
Milvus 的生态是我觉得它比一些竞品更“好用”的原因之一。Attu 是官方提供的可视化管理工具,用 Docker 起一个容器就能连上 Milvus,Collection 列表、Schema、数据预览、索引状态、慢查询都能看到。我排查问题的时候经常开着 Attu,比写代码查方便。它还有一个内置的向量搜索界面,可以手动输入向量或者用文本调 Embedding 模型后搜索,调试的时候很直观。
LangChain 和 LlamaIndex 是两个我用得最多的上层框架。它们都封装了 Milvus 的 VectorStore 接口,你只要传连接信息和 Collection 名称,剩下的 embedding、插入、检索都帮你处理好了。我用 LangChain 搭过一个 RAG 的最小示例:把一篇 PDF 切成 chunk,用 OpenAI Embedding 模型生成向量存进 Milvus,用户提问时把问题也 embedding 一下,去 Milvus 搜 top 5 相关 chunk,拼进 prompt 里交给 LLM 回答。整个流程不到一百行代码,效果比纯 LLM 好很多,因为它有了外部知识。
LlamaIndex 在文档切分和索引管理上做得更细一些,它支持多种 NodeParser,能处理 Markdown、代码、表格等不同格式。我做企业知识库的时候用 LlamaIndex 做 ingestion,用 Milvus 做向量存储,配合它的 ResponseSynthesizer 生成回答。这两个框架都在快速迭代,API 偶尔会变,建议锁定版本并在小项目里先跑通。RAG 最小示例跑通之后,你会发现检索质量主要取决于切分策略、Embedding 模型和 top_k 的选择,向量数据库本身只是其中一个环节。
1.9 常见问题与学习路径:维度与归一化、索引参数调优、性能排查、进阶方向
我遇到最多的问题就是维度和归一化对不上。Embedding 模型输出 768 维,你建 Collection 时写了 512,插入直接报错。或者模型输出已经归一化了,你用 L2 度量去搜,结果排序不符合预期。我的习惯是拿到一个 Embedding 模型先看它的文档,确认输出维度和是否建议归一化,然后在建 Collection 时把维度和度量方式一次性定对。归一化这件事,如果不确定,就先做一次 numpy.linalg.norm 检查,再决定用 IP 还是 COSINE。
索引参数调优没有万能公式。我的做法是先跑一组基准:固定数据集和查询集,测不同 nlist、nprobe、M、efConstruction 组合下的召回率和 QPS,画一条召回-延迟曲线,选那个在可接受召回率下延迟最低的点。性能排查的时候,我会按这个顺序看:先看 Query Node 的 CPU 和内存是不是瓶颈,再看 segment 数量是不是太多(小 segment 多了合并开销大),然后看索引是否已经建好并加载,最后看一致性级别和过滤条件是不是太苛刻。慢查询日志和 Attu 的监控面板能帮你快速定位。
学习路径上,我建议先把 1.5 节的快速上手跑通,理解 Collection、索引、搜索这三件事的关系。然后深入 1.6 节,把不同索引和度量方式都试一遍,感受它们的差异。再往后学 Partition、一致性、批量导入这些运维能力。进阶方向可以看这几个:多向量字段和混合检索、稀疏向量(BM25)和稠密向量的混合搜索、Milvus 的 Serverless 模式、以及和 Kafka/Flink 做实时向量管道。如果你要做 RAG,把 1.8 节的生态集成吃透,再回去看索引和一致性,会理解得更深。
2.1 对比前提:Faiss 作为向量检索库与 Milvus 作为向量数据库的差异
早几年做相似度检索,我手上只有一个 Faiss,那会儿觉得它无所不能。装个 faiss-cpu,几十行代码就能在百万向量上跑出毫秒级查询。项目变复杂之后,要支持在线增删、多用户、持久化、监控,我才发现 Faiss 在这些方面几乎都要自己写。Milvus 出现之后,我把一部分场景迁了过去。这两者放一起比性能,前提得先摆清楚:Faiss 是一个向量检索算法库,Milvus 是一套完整的向量数据库系统。拿库和系统直接比 QPS,容易得出偏颇的结论。
Faiss 由 Meta 开源,核心价值在于把 ANN 算法做到极致,索引类型丰富,CPU 和 GPU 版本都有,C++ 底层加 Python 封装,性能调优空间大。它不负责数据持久化,不提供网络服务,不管多租户和权限,这些都是使用者的事。Milvus 把这些工程问题都包进去了,代价是系统本身有组件开销。我心里有个预期:同一份数据、同一种索引,Milvus 的单次查询延迟大概率比 Faiss 高一点点,因为它多了网络传输、请求路由、结果聚合这些步骤。差距有多大,得看数据规模和部署方式。
把 Faiss 理解成发动机,把 Milvus 理解成整车,这个类比我一直觉得挺贴切。发动机单独测功率有优势,整车要考虑变速箱、底盘、油耗、安全性。选哪个不取决于谁跑得快,取决于你要的是零件还是能上路的产品。
2.2 评测方法与基准设计:数据集、召回率、QPS、P99 延迟、内存与成本指标
设计对比评测的时候,第一步是选数据集。公开数据集里我用过 SIFT1M、GIST1M、Deep1B 的子集,它们的好处是大家都有,结果可复现。业务数据集我也用过,比如一批商品图的 Embedding。公开数据集的好处是别人能验证你的结论,业务数据的好处是能反映真实分布。两种我都会跑,对外展示用公开数据集,内部决策用业务数据。
指标方面,我关注四个:召回率、QPS、P99 延迟、资源占用。召回率 @k 是核心,它衡量返回的 top-k 里有多少是真正的最近邻。没有召回率约束的 QPS 没有意义,把 efSearch 调到 1,QPS 能翻十倍,结果全是错的。QPS 我会同时看单线程和多线程,单线程反映算法本身的效率,多线程反映系统并发能力。P99 延迟比平均延迟更能暴露问题,平均值好看但尾部抖动大的系统,线上体验会很差。
资源占用我一般记录峰值内存、CPU 利用率、GPU 显存、磁盘 IO。成本指标换算成每百万向量的月成本,包括机器费用和运维人力。评测时我会固定硬件环境,同一台机器、同一个数据集、同一组查询向量,参数做网格搜索找各自的最优点。直接拿默认参数比,结论往往不靠谱。
2.3 索引与算法能力对比:索引类型、量化、GPU 加速、过滤检索与更新能力
Faiss 的索引库是我见过最全的。IVF 系列、HNSW、PQ、OPQ、LSH、ScalarQuantizer,组合起来能玩出很多花样。它还有 IndexShards、IndexIVFShards 这种多索引组合方式,可以手动做分片检索。GPU 版本覆盖了大部分常用索引,Cagra 图索引在 GPU 上的表现非常亮眼。我拿 Faiss GPU 跑过一批 1000 万 128 维向量,单卡 QPS 能到几万,延迟也很低。
Milvus 的索引是封装过的,常用类型都有:FLAT、IVF_FLAT、IVF_SQ8、IVF_PQ、HNSW、DiskANN、SCANN,GPU 索引有 GPU_IVF_FLAT、GPU_IVF_PQ、GPU_CAGRA。它把参数做了简化,比如 HNSW 主要调 M 和 efConstruction。功能上 Milvus 多出来的是过滤检索,可以在向量搜索的同时加标量条件,Faiss 原生不支持这个,要用 IDSelector 之类的机制自己实现,性能损耗也不小。
更新能力是两者差异明显的地方。Faiss 的索引大部分偏静态,插入新向量需要重建或者用 IndexIDMap 加 add_with_ids,删除要靠 remove_ids,底层实现有限。Milvus 支持在线插入、删除、更新,后台会自动合并 segment 和重建索引。我做实时推荐召回的时候,这个能力很关键,用户行为每分钟都在产生新向量,Faiss 方案得写一堆调度逻辑。
2.4 单机性能对比:中小规模向量下的查询延迟、吞吐与召回表现
中小规模,我一般指一百万到一千万向量,维度在 128 到 768 之间。这个区间里,Faiss 在纯查询延迟上有明显优势。我做过一组对比,100 万 768 维向量,HNSW 索引,Faiss 单线程 P50 大概 0.3 毫秒,Milvus 是 1.2 毫秒左右。差的那一毫秒,主要是网络往返和 proxy 处理。要是把 Milvus 部署在应用同机,用 Unix socket 连接,延迟能压到 0.8 毫秒上下。
QPS 方面,多线程压测下 Faiss 能到 5000 以上,Milvus 在 Standalone 模式下大概 2000 到 3000。把 Milvus 的查询并发参数调一调,差距能缩小一些。召回率两边可以对齐,同样的 HNSW 参数下,两者返回结果的重合度很高,因为底层算法是相通的。我做过逐条比对,召回率差距在千分之几的量级,主要是浮点运算顺序不同带来的。
内存占用上,Faiss 更省。它只加载索引结构和向量数据,没有额外的元数据、日志、缓存。Milvus 的进程里有 segment 元信息、查询缓存、协调组件,基线内存就要几百 MB。百万级数据下,这个差异可以接受。我笔记本上跑 Milvus Standalone 加 100 万向量,内存占用 2GB 左右,Faiss 同样的数据只要 1GB 出头。
2.5 大规模与分布式性能对比:亿级向量、水平扩展、高可用与一致性
向量数量上到亿级,情况就变了。Faiss 单机的瓶颈是内存。1 亿条 768 维 float32 向量,光原始数据就是 280GB 左右,就算用 PQ 压缩到 1/8,也要 35GB。加上索引结构,单台机器很难扛住。Faiss 有 IndexShards 可以做多机分片,但分片逻辑、结果合并、故障恢复都要自己写。我见过有人用 Spark 调度 Faiss 分片,写了几千行胶水代码,维护起来很痛苦。
Milvus 从设计上就是为分布式准备的。数据分片存在多个 Query Node 上,查询并行执行后合并结果。加节点就能扩容量和吞吐,接近线性扩展。我跑过一次 5000 万向量的集群测试,三个 Query Node,整体 QPS 比单节点翻了接近两倍。亿级向量的场景我也见过客户在用,几十个节点,延迟稳定在几十毫秒。
高可用和一致性是分布式绕不开的问题。Milvus 通过副本机制保证可用性,Query Node 挂了会自动切换。一致性级别可以调,强一致适合金融类场景,最终一致适合日志和推荐。Faiss 没有这些概念,都是使用方自己要解决的工程问题。到了这个规模,Milvus 的优势就不只是性能了,是它能让你不用从零造一套分布式检索系统。
2.6 工程化与功能对比:持久化、增删改查、多租户、权限、监控与生态
持久化这块,Faiss 的索引可以 write_index 存到文件,也可以做内存映射。它没有事务,写入过程中断电可能损坏索引文件。Milvus 有完整的 WAL 机制,数据先写日志再落盘,崩溃恢复有保障。我做过一次断电测试,Milvus 重启后数据完整,索引自动恢复,这个过程没让我手动干预。
增删改查,Faiss 的 API 偏低阶。插入用 add,删除用 remove_ids,更新通常先删后加。批量删除后索引里会有空洞,需要定期 compact。Milvus 提供标准的 CRUD 接口,删除是逻辑删除,后台会做 compaction。多租户和权限 Faiss 完全没有,Milvus 有 RBAC,可以给不同用户和角色分配 Collection 级别的权限。
监控和生态是 Milvus 的强项。Prometheus 指标、Grafana 面板、慢查询日志、Attu 可视化,一套都齐了。LangChain、LlamaIndex、Spring AI、Haystack 这些框架都原生支持 Milvus。Faiss 更多是被当作算法组件嵌进其他系统,比如 LangChain 里的 FAISS vectorstore,本质上还是本地文件加内存索引。做企业应用的时候,这些工程能力往往比那几毫秒延迟差更值钱。
2.7 资源消耗与成本对比:CPU/GPU、内存、磁盘、运维复杂度和总体拥有成本
机器成本上,Faiss 更轻。一台 32 核 128GB 内存的机器,跑千万级向量很轻松。Milvus Standalone 同样的数据要留更多内存余量,组件开销加上索引加载,建议 64GB 起步。集群模式组件多,至少三台机器起步,运维成本也跟着上来。
GPU 场景下差距会更明显。Faiss GPU 可以独占一张卡跑到极致,Milvus 的 GPU 索引也支持,但资源调度是统一管理的,一张卡可能被多个 Collection 共享。好处是利用率高,坏处是单个任务的峰值性能不如独占。我做过对比,同样的 A100,Faiss GPU 单 Collection QPS 比 Milvus GPU 高 20% 到 30%。
总体拥有成本不能只看机器。Faiss 方案要自己写持久化、监控、分布式分片、权限,这部分人力成本可能比机器贵得多。我估算过一个中型项目,自研 Faiss 方案的初始开发要两到三个月,加上后续维护,一年的人力成本折算下来能买不少服务器。Milvus 的运维复杂度主要在集群部署和调优,但它提供的东西是现成的。
2.8 选型建议与典型场景:研究原型、生产 RAG、推荐搜索、混合架构实践
研究原型和离线实验,我基本都用 Faiss。跑 benchmark、试新索引、做算法验证,它启动快、依赖少、调参灵活。写论文或者做技术评估的时候,Faiss 是首选。数据量不大、单机部署、没有在线服务要求的场景,Faiss 也够用。
生产 RAG、推荐召回、图像搜索、多租户 SaaS,这些场景我推荐 Milvus。RAG 需要和 LangChain 之类的框架集成,需要持久化和在线更新,需要多用户隔离,Milvus 都覆盖了。我做过一个企业知识库项目,几十万文档,日活几千人,用 Milvus Standalone 跑得很稳,运维基本不用操心。
混合架构我也用过。粗排阶段用 Milvus 从亿级数据里召回几万个候选,精排阶段用 Faiss GPU 在小候选集上做高精度重排序。这种组合把两者的优势都用上了。还有一种常见做法是用 Faiss 做离线批量计算,把结果灌进 Milvus 做在线服务。选型不是二选一,看你需要什么层次的能力。
2.9 性能对比结论与优化清单:避免误比较、参数调优、压测复现与持续基准
我见过不少拿 Faiss 和 Milvus 对比的文章,结论差异很大。很大一部分原因是指标设置不一样。有人拿 Faiss 的 HNSW 和 Milvus 的 IVF_FLAT 比,有人用的召回率不同,有人没关掉 Milvus 的日志和监控。做对比的时候,索引类型、参数、召回率、硬件、并发数都要对齐,否则结论没意义。
参数调优对两个系统的影响都很大。Faiss 的 nprobe、efSearch、nlist 要针对数据集调。Milvus 的 ef、nprobe、search_list_size 同理,还要看 segment 数量、索引是否加载、一致性级别。我调优的习惯是先固定召回率目标,比如 95%,然后在这个约束下优化延迟。单纯看 QPS 峰值容易误导。
压测要能复现,我一般会写好脚本,固定数据集路径、查询集、参数网格,跑完输出 CSV 和曲线图。持续基准也很重要,每次版本升级、配置变更后重新跑一遍,观察有没有回归。优化清单我总结了这么几条:索引类型和度量方式匹配数据特征,参数用网格搜索找最优点,批量查询代替单条查询,过滤条件下推到查询层,生产环境调高 Query Node 并行度,监控 P99 而不是平均值。这些做到位,两者的性能差距会缩小到可接受范围,选型时就能把重点放回工程能力和业务需求上。