Milvus 和 Elasticsearch、OpenSearch 对比:向量检索怎么选

Milvus 和 Elasticsearch 对比,先看出身:Milvus 是为向量检索而生的开源数据库,Elasticsearch 和 OpenSearch 是在 Lucene 搜索引擎上加了向量字段。三者都能做 BM25 和混合检索,差别在规模上来之后的吞吐、延迟、索引选择和成本。向量检索是主业务(RAG、语义搜索、Agent),选 Milvus;以日志和分析为主,继续用 Elasticsearch 或 OpenSearch。

  • Apache License 2.0
  • GitHub 40K+ star
  • 内置 BM25 全文检索
  • HNSW、IVF、RaBitQ、DiskANN、GPU 索引
  • VTS 支持从 Elasticsearch 迁移
Milvus 用自己的向量索引引擎,存算分离;Elasticsearch / OpenSearch 在每个分片的每个 Lucene segment 里各建一个 HNSW 图。
Milvus 用自己的向量索引引擎,存算分离;Elasticsearch / OpenSearch 在每个分片的每个 Lucene segment 里各建一个 HNSW 图。
Milvus 与 Zilliz Cloud 用户
  • Salesforce
  • Walmart
  • Reddit
  • NVIDIA
  • eBay
  • Shell
  • Roblox
  • IBM

Milvus 和 Elasticsearch 性能对比:向量检索谁更快?

Milvus 与 ES 性能对比看 VectorDBBench 排行榜:无论纯向量检索还是带过滤的检索,Zilliz Cloud 和自建 Milvus 的 QPS 都是 OpenSearch、Elastic Cloud 的数倍,P99 延迟只有它们的一小部分。

向量检索吞吐(QPS,越高越好)Cohere 1M 数据集,768 维
  • Zilliz CloudZillizCloud-8cu-perf13,316.23 QPSP99 2 ms · 召回率 0.9383
  • MilvusMilvus-16c64g-sq4u-fp16-force_merge9,575.69 QPSP99 2.3 ms · 召回率 0.9189
  • OpenSearchOpenSearch-16c128g-force_merge3,055.01 QPSP99 7.2 ms · 召回率 0.9066
  • Elastic CloudElasticCloud-8c60g-force_merge2,353.89 QPSP99 17.1 ms · 召回率 0.9143

来源:VectorDBBench,配置标签按原始结果列出。结果随配置变化,请在自己的数据上复测。

过滤检索吞吐(QPS,过滤后保留 1% 数据)Cohere 1M 数据集,768 维,filter ratio 0.99
  • Zilliz CloudZillizCloud-8cu-perf-partition_key10,020.53 QPSP99 2.6 ms · 召回率 0.9788
  • MilvusMilvus-16c64g-sq8-partition_key11,280.08 QPSP99 1.6 ms · 召回率 0.9507
  • OpenSearchOpenSearch-16c128g-routing-64shard-force_merge3,064.63 QPSP99 6.5 ms · 召回率 0.9507
  • Elastic CloudElasticCloud-8c60g-routing-64shard2,789.72 QPSP99 8.2 ms · 召回率 0.9538

来源:VectorDBBench 排行榜(zilliz.com/vdbbench-leaderboard),系统标签原样引用。结果因配置而异,建议用自己的数据跑一遍。

Zilliz Cloud 就是全托管的 Milvus:同样的引擎、同一梯队的性能,不用自己规划集群、升级和扩容。

免费试用 Zilliz Cloud

Milvus、Elasticsearch、OpenSearch 一览:许可证、社区、客户端与上限

ES 与 Milvus 在向量检索上有何核心区别?Elasticsearch 和 OpenSearch 在每个 Lucene segment 里建 HNSW 图,跑在 JVM 数据节点上,Elastic 的调优文档建议把大部分向量数据留在内存;Milvus 的 query 节点从对象存储加载 segment,tiered storage 淘汰冷数据,内存按热数据而不是全量语料来配。

向量数据库与 Elasticsearch、OpenSearch 的项目事实(依据官方文档与代码仓库)
MilvusElasticsearchOpenSearch
许可证Apache-2.0;LF AI & Data 毕业项目ELv2、SSPL 或 AGPLv3Apache-2.0;OpenSearch Software Foundation(Linux 基金会)
GitHub star40K+70K+10K+
实现语言Go 控制面,C++ 查询引擎Java(Lucene)Java(Lucene)
向量最高维度32,7684,096(dense_vector)16,000(knn_vector)
免费版的混合检索hybrid_search 配 RRFRanker 或 WeightedRankerRRF retriever 列在 Enterprise 订阅search pipeline 的 normalization 或 RRF processor
客户端与框架Python、Java、Go、Node.js、C#、C++、Rust、REST;langchain-milvus、LlamaIndex、Haystack、DifyREST 与官方语言客户端;langchain-elasticsearchREST 与官方语言客户端
部署方式Lite(pip)、Standalone(单容器)、Distributed(Kubernetes)、Zilliz Cloud自建或 Elastic Cloud自建或 Amazon OpenSearch Service

Milvus、Elasticsearch、OpenSearch 各支持哪些向量索引?

Elasticsearch 在每个 Lucene segment 上建 HNSW 或 flat 索引,支持 int8、int4 和 BBQ 量化;OpenSearch 另有 Faiss IVF 和 on_disk 模式。Milvus 的选择更多,包括 GPU_CAGRA、GPU_IVF_FLAT、GPU_IVF_PQ、GPU_BRUTE_FORCE,以及面向过滤的标量索引,可以按字段权衡内存、召回率和延迟。

向量索引类型对比(依据 Elasticsearch dense_vector、OpenSearch knn_vector、Milvus index-explained 官方文档)
索引类别MilvusElasticsearchOpenSearch
图索引(HNSW)HNSW、HNSW_SQ、HNSW_PQ、HNSW_PRQhnsw、int8_hnsw、int4_hnsw、bbq_hnswFaiss 或 Lucene 引擎上的 hnsw
聚类 / 量化索引IVF_FLAT、IVF_SQ8、IVF_PQ、IVF_RABITQ、SCANNflat、int8_flat、int4_flat、bbq_flatFaiss ivf;sq、pq 与二值量化
磁盘索引DISKANN;从对象存储按需加载的 tiered storagebbq_disk(需 Enterprise 订阅)on_disk 模式,支持压缩级别与 rescoring
稀疏向量与 BM25SPARSE_INVERTED_INDEX(BM25 或 IP 度量)sparse_vector(ELSER)、Lucene BM25Neural sparse search、Lucene BM25
二值向量BIN_FLAT、BIN_IVF_FLAT、MINHASH_LSHbit 类型,配 hnsw 或 flat二值量化
标量与 JSON 过滤INVERTED、BITMAP、STL_SORT、Trie、NGRAM、RTREE、JSON path 索引由 mapping 决定的 Lucene 字段索引由 mapping 决定的 Lucene 字段索引

专用向量数据库比搜索引擎多了什么?

ES 向量检索 vs Milvus,差别在系统围绕什么构建。如果文档本来就在 ES 索引里,Elasticsearch 向量检索够用。Milvus 把 collection schema 当成核心:类型化的向量、JSON 和数组,开源版内置 BM25 与混合检索,架构上存算分离,各类节点独立扩缩容。对于多租户 SaaS、RAG 知识库和 Agent 记忆这类“向量是主数据”的场景,这些能力决定了上线后的运维成本。

Milvus 面向向量与混合检索的能力(milvus.io 文档)
能力Milvus 的做法
多租户database(默认 64 个)、collection(默认 65,536 个)、partition(每个 collection 1,024 个),或用 partition key 支撑百万级租户
JSON 与动态字段JSON 字段可建 JSON path 索引;dynamic field 存放 schema 外的键
ARRAY 字段类型化数组,支持 ARRAY_CONTAINS 过滤和 BITMAP / INVERTED 索引
多向量混合检索最多 10 个向量字段;hybrid_search 用 RRF 或加权 reranker 融合稠密向量、稀疏向量与 BM25 结果
BM25 全文检索BM25 function 搭配 standard、english、chinese、ICU 或自定义分词器;直接用原始文本查询
关键词与短语匹配在分词后的 VARCHAR 字段上用 TEXT_MATCH、PHRASE_MATCH(支持 slop)
模式过滤LIKE 与 RE2 正则(=~、!~),可用 NGRAM 索引加速
部分更新与 TTLupsert 设 partial_update=True 只改传入字段;collection.ttl.seconds 让数据自动过期
存算分离Query、Streaming、Data 节点共享对象存储,独立扩缩容
Tiered storage热数据按需加载、冷数据淘汰,Query 节点内存占用更低

ES / OpenSearch 字段如何映射到 Milvus schema?

多数字段可以一一对应,稠密向量、稀疏向量在 Milvus 里都有原生类型;text 字段变成开启分词的 VARCHAR 加 BM25 function,关键词相关性迁过来不丢。Milvus 文档也给出了查询 DSL 的对应关系:match 对应全文检索,term 对应 == 或 TEXT_MATCH,prefix、wildcard 对应 LIKE,knn 对应 search,RRF 对应 hybrid_search。

ES 迁移 Milvus 字段映射表
Elasticsearch / OpenSearchMilvus
IndexCollection(位于某个 database 中)
_idVARCHAR 或 INT64 主键;双写期间保持同一 ID
dense_vector / knn_vectorFLOAT_VECTOR(或 FLOAT16、BFLOAT16、INT8),维度与度量保持一致
带 analyzer 的 textenable_analyzer 的 VARCHAR + BM25 function 输出 SPARSE_FLOAT_VECTOR
keywordVARCHAR 配 INVERTED 或 BITMAP 索引;wildcard 用 NGRAM
数值与日期INT64 / DOUBLE 配 STL_SORT 或 INVERTED 索引
keyword 数组VARCHAR 类型的 ARRAY
object、nested、flattenedJSON 字段加 JSON path 索引,或 dynamic field
sparse_vectorSPARSE_FLOAT_VECTOR,IP 度量
按租户自定义 routingPartition key

如何从 Elasticsearch 或 OpenSearch 迁移到 Milvus?

先迁检索链路,旧集群继续服务,直到结果对齐。VTS(Vector Transport Service)把 Elasticsearch 列为源端连接器;OpenSearch 可以用 scroll API 导出,再用 BulkWriter 生成文件导入。全程保留旧集群作为回滚路径,切流量只是改配置,不需要反向迁移数据。

  • 导出:VTS es_to_milvus,或用 scroll / point-in-time 读取 _source。
  • 建 schema:映射字段,加 BM25 function,给过滤字段建索引。
  • 回填:BulkWriter 加 bulk_import;小数据量直接 insert。
  • 双写:新文档从同一数据源同时写入两边。
  • 校验:用评测查询集对比召回率与相关性,再逐步切读流量。

Milvus 还是 Elasticsearch:怎么选,以及 ES 更合适的场景

客观地说,Milvus 和 ES 对比不是非此即彼:搜索只是现有 Elastic 栈的附属功能时,直接在 ES 里加向量也可以。运维也不同:Elasticsearch 要调 JVM 堆和分片规划;Milvus Standalone 是一个内嵌 etcd 的容器,Distributed 在 Kubernetes 上用 Milvus Operator 部署。成本跟着内存走:ES 把 HNSW 图放在数据节点上,Milvus 可以把冷 segment 留在对象存储。Elasticsearch 与 Milvus 作为 RAG 向量库怎么选?按下表对号。

选型清单:按场景看 ES 和 Milvus 选谁
你的情况选择
向量检索就是产品本身:RAG、语义搜索、AgentMilvus
基于 Kibana 或 OpenSearch Dashboards 的日志、可观测性、安全分析Elasticsearch 或 OpenSearch
以聚合和分面统计为主的文本分析,向量只是小补充Elasticsearch 或 OpenSearch
已有 ELK 链路,向量数据量不大Elasticsearch 或 OpenSearch
语料增长到数据节点内存装不下Milvus,用 tiered storage 或 DiskANN
单个 collection 里有百万级租户Milvus partition key
要在 Apache-2.0 版本里做 BM25 加向量融合Milvus 或 OpenSearch
两类需求都有ES 做分析,Milvus 做检索,按文档 ID 关联

用 Python 把 Elasticsearch 索引映射为 Milvus collection

下面的脚本读取 Elasticsearch 索引中的全部文档,写入一个包含稠密向量、BM25、keyword、数组和 JSON 字段的 Milvus collection。

python
# pip install -U pymilvus elasticsearch
from elasticsearch import Elasticsearch, helpers
from pymilvus import MilvusClient, DataType, Function, FunctionType

es = Elasticsearch("http://localhost:9200")
client = MilvusClient(uri="http://localhost:19530")

# ES mapping: title(text) category(keyword) tags(keyword[]) attrs(object) embedding(dense_vector, 768)
schema = client.create_schema(enable_dynamic_field=True)
schema.add_field("id", DataType.VARCHAR, is_primary=True, max_length=64)  # ES _id
schema.add_field("title", DataType.VARCHAR, max_length=8192, enable_analyzer=True, enable_match=True)
schema.add_field("title_bm25", DataType.SPARSE_FLOAT_VECTOR)  # filled by the BM25 function
schema.add_field("category", DataType.VARCHAR, max_length=128)
schema.add_field("tags", DataType.ARRAY, element_type=DataType.VARCHAR, max_capacity=64, max_length=128)
schema.add_field("attrs", DataType.JSON, nullable=True)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=768)
schema.add_function(Function(name="title_bm25_fn", function_type=FunctionType.BM25,
                             input_field_names=["title"], output_field_names=["title_bm25"]))

idx = client.prepare_index_params()
idx.add_index(field_name="embedding", index_type="HNSW", metric_type="COSINE",
              params={"M": 16, "efConstruction": 200})
idx.add_index(field_name="title_bm25", index_type="SPARSE_INVERTED_INDEX", metric_type="BM25")
idx.add_index(field_name="category", index_type="INVERTED")
idx.add_index(field_name="attrs", index_type="INVERTED", index_name="attrs_brand",
              params={"json_path": 'attrs["brand"]', "json_cast_type": "varchar"})
client.create_collection("docs", schema=schema, index_params=idx)

batch = []
for hit in helpers.scan(es, index="docs", query={"query": {"match_all": {}}}):
    src = hit["_source"]
    batch.append({"id": hit["_id"], "title": src["title"], "category": src["category"],
                  "tags": src.get("tags", []), "attrs": src.get("attrs"),
                  "embedding": src["embedding"]})
    if len(batch) == 1000:
        client.insert("docs", batch)
        batch = []
if batch:
    client.insert("docs", batch)

dim 和 metric_type 要与 dense_vector mapping 一致。数据量大时,用 BulkWriter 生成文件、bulk_import 导入,替代 insert。

常见问题

ES 能做向量数据库吗?

能,ES 支持向量检索,不少团队也把 Elasticsearch 当向量数据库用:dense_vector 存 embedding,knn 查询在每个 Lucene segment 的 HNSW 或 flat 索引上检索。Milvus 则是围绕向量索引构建的数据库,BM25 和过滤在同一个 collection 内完成。

Milvus 和 ES 向量检索性能差多少?

VectorDBBench Cohere 1M 测试中,Milvus-16c64g-sq4u-fp16-force_merge 为 9,575.69 QPS(召回率 0.9189),ElasticCloud-8c60g-force_merge 为 2,353.89 QPS(0.9143)。各行硬件不同,请用自己的数据测试。

Milvus 支持 BM25 全文检索吗,能替代 ES 吗?

检索场景可以:BM25 function 在写入时把分词后的文本转成稀疏向量,查询直接传原始文本,可单独用也可做混合检索。Kibana 看板和重聚合分析仍留在 Elasticsearch。

Elasticsearch 和 Milvus 的混合检索有什么区别?

ES 向量检索和 BM25 混用靠 RRF retriever 融合,Elastic 订阅页将其列在 Enterprise 级别;OpenSearch 用 search pipeline 处理器。Milvus 开源版直接用 hybrid_search 搭配 RRFRanker 或 WeightedRanker。

ES 数据怎么迁移到 Milvus?

把 dense_vector 映射为维度、度量一致的 FLOAT_VECTOR,再用 VTS 或 scroll 导出加 BulkWriter 搬数据。切换期间双写,召回率对齐后再切读流量。

团队已有 ES 集群,还需要 Milvus 吗?

看向量是不是主负载。常见做法是并存:ES 继续做日志和分析,向量与混合检索交给 Milvus,两边从同一数据源写入,按文档 ID 关联结果。

不想自己运维集群?

Zilliz Cloud 是全托管的 Milvus。VectorDBBench Cohere 1M 测试中,ZillizCloud-8cu-perf 为 13,316.23 QPS(召回率 0.9383),MilvusClient API 与开源 Milvus 一致。

资料来源与技术文档 (53)