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 迁移
- Salesforce
- Walmart
- NVIDIA
- eBay
- Shell
- Roblox
- IBM
Milvus 和 Elasticsearch 性能对比:向量检索谁更快?
Milvus 与 ES 性能对比看 VectorDBBench 排行榜:无论纯向量检索还是带过滤的检索,Zilliz Cloud 和自建 Milvus 的 QPS 都是 OpenSearch、Elastic Cloud 的数倍,P99 延迟只有它们的一小部分。
- 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
- 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 CloudMilvus、Elasticsearch、OpenSearch 一览:许可证、社区、客户端与上限
ES 与 Milvus 在向量检索上有何核心区别?Elasticsearch 和 OpenSearch 在每个 Lucene segment 里建 HNSW 图,跑在 JVM 数据节点上,Elastic 的调优文档建议把大部分向量数据留在内存;Milvus 的 query 节点从对象存储加载 segment,tiered storage 淘汰冷数据,内存按热数据而不是全量语料来配。
| Milvus | Elasticsearch | OpenSearch | |
|---|---|---|---|
| 许可证 | Apache-2.0;LF AI & Data 毕业项目 | ELv2、SSPL 或 AGPLv3 | Apache-2.0;OpenSearch Software Foundation(Linux 基金会) |
| GitHub star | 40K+ | 70K+ | 10K+ |
| 实现语言 | Go 控制面,C++ 查询引擎 | Java(Lucene) | Java(Lucene) |
| 向量最高维度 | 32,768 | 4,096(dense_vector) | 16,000(knn_vector) |
| 免费版的混合检索 | hybrid_search 配 RRFRanker 或 WeightedRanker | RRF retriever 列在 Enterprise 订阅 | search pipeline 的 normalization 或 RRF processor |
| 客户端与框架 | Python、Java、Go、Node.js、C#、C++、Rust、REST;langchain-milvus、LlamaIndex、Haystack、Dify | REST 与官方语言客户端;langchain-elasticsearch | REST 与官方语言客户端 |
| 部署方式 | 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,以及面向过滤的标量索引,可以按字段权衡内存、召回率和延迟。
| 索引类别 | Milvus | Elasticsearch | OpenSearch |
|---|---|---|---|
| 图索引(HNSW) | HNSW、HNSW_SQ、HNSW_PQ、HNSW_PRQ | hnsw、int8_hnsw、int4_hnsw、bbq_hnsw | Faiss 或 Lucene 引擎上的 hnsw |
| 聚类 / 量化索引 | IVF_FLAT、IVF_SQ8、IVF_PQ、IVF_RABITQ、SCANN | flat、int8_flat、int4_flat、bbq_flat | Faiss ivf;sq、pq 与二值量化 |
| 磁盘索引 | DISKANN;从对象存储按需加载的 tiered storage | bbq_disk(需 Enterprise 订阅) | on_disk 模式,支持压缩级别与 rescoring |
| 稀疏向量与 BM25 | SPARSE_INVERTED_INDEX(BM25 或 IP 度量) | sparse_vector(ELSER)、Lucene BM25 | Neural sparse search、Lucene BM25 |
| 二值向量 | BIN_FLAT、BIN_IVF_FLAT、MINHASH_LSH | bit 类型,配 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 的做法 |
|---|---|
| 多租户 | 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 索引加速 |
| 部分更新与 TTL | upsert 设 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。
| Elasticsearch / OpenSearch | Milvus |
|---|---|
| Index | Collection(位于某个 database 中) |
| _id | VARCHAR 或 INT64 主键;双写期间保持同一 ID |
| dense_vector / knn_vector | FLOAT_VECTOR(或 FLOAT16、BFLOAT16、INT8),维度与度量保持一致 |
| 带 analyzer 的 text | enable_analyzer 的 VARCHAR + BM25 function 输出 SPARSE_FLOAT_VECTOR |
| keyword | VARCHAR 配 INVERTED 或 BITMAP 索引;wildcard 用 NGRAM |
| 数值与日期 | INT64 / DOUBLE 配 STL_SORT 或 INVERTED 索引 |
| keyword 数组 | VARCHAR 类型的 ARRAY |
| object、nested、flattened | JSON 字段加 JSON path 索引,或 dynamic field |
| sparse_vector | SPARSE_FLOAT_VECTOR,IP 度量 |
| 按租户自定义 routing | Partition 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 向量库怎么选?按下表对号。
| 你的情况 | 选择 |
|---|---|
| 向量检索就是产品本身:RAG、语义搜索、Agent | Milvus |
| 基于 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。
# 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。
延伸阅读
- How should you benchmark vector search performance?
- What is full-text search, and how does BM25 fit?
- What is hybrid search, and when should you use it?
- How does a sparse inverted index differ from dense ANN?
- How do HNSW_SQ, HNSW_PQ, and HNSW_PRQ change the trade-offs?
- What is IVF_RABITQ, and how should you evaluate it?
- When should you use DISKANN in Milvus?
- How do bulk migration, backup, and Milvus CDC differ?
常见问题
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)
- https://zilliz.com/vdbbench-leaderboard
- https://github.com/zilliztech/VectorDBBench
- https://github.com/zilliztech/VectorDBBench/blob/main/vectordb_bench/results/leaderboard_v2.json
- https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector
- https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/sparse-vector
- https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion
- https://www.elastic.co/subscriptions
- https://docs.opensearch.org/latest/mappings/supported-field-types/knn-methods-engines/
- https://docs.opensearch.org/latest/vector-search/optimizing-storage/disk-based-vector-search/
- https://docs.opensearch.org/latest/vector-search/optimizing-storage/binary-quantization/
- https://docs.opensearch.org/latest/vector-search/ai-search/hybrid-search/rrf/
- https://docs.opensearch.org/latest/search-plugins/search-pipelines/normalization-processor/
- https://github.com/milvus-io/milvus
- https://milvus.io/docs/index-explained.md
- https://milvus.io/docs/ivf-rabitq.md
- https://milvus.io/docs/scann.md
- https://milvus.io/docs/bitmap.md
- https://milvus.io/docs/ngram.md
- https://milvus.io/docs/json-indexing.md
- https://milvus.io/docs/multi_tenancy.md
- https://milvus.io/docs/use-json-fields.md
- https://milvus.io/docs/enable-dynamic-field.md
- https://milvus.io/docs/array_data_type.md
- https://milvus.io/docs/multi-vector-search.md
- https://milvus.io/docs/full-text-search.md
- https://milvus.io/docs/analyzer-overview.md
- https://milvus.io/docs/keyword-match.md
- https://milvus.io/docs/phrase-match.md
- https://milvus.io/docs/basic-operators.md
- https://milvus.io/docs/upsert-entities.md
- https://milvus.io/docs/set-collection-ttl.md
- https://milvus.io/docs/architecture_overview.md
- https://milvus.io/docs/tiered-storage-overview.md
- https://milvus.io/docs/prepare-source-data.md
- https://milvus.io/docs/import-data.md
- https://milvus.io/docs/elasticsearch-queries-to-milvus.md
- https://github.com/zilliztech/vts
- https://milvus.io/docs/quickstart.md
- https://milvus.io/docs/zh/quickstart.md
- https://zilliz.com.cn/cloud
- https://www.elastic.co/pricing/faq/licensing
- https://www.elastic.co/docs/deploy-manage/production-guidance/optimize-performance/approximate-knn-search
- https://opensearch.org/faq/
- https://www.linuxfoundation.org/press/linux-foundation-announces-opensearch-software-foundation
- https://github.com/elastic/elasticsearch
- https://github.com/opensearch-project/OpenSearch
- https://docs.langchain.com/oss/python/integrations/vectorstores/milvus
- https://docs.langchain.com/oss/python/integrations/vectorstores/elasticsearch
- https://milvus.io/docs/integrations_overview.md
- https://milvus.io/docs/install-overview.md
- https://milvus.io/docs/install_standalone-docker.md
- https://milvus.io/docs/limitations.md
- https://lfaidata.foundation/projects/