卓普云

Weaviate vs. OpenSearch vs. pgvector:向量搜索数据库对比

本文对比OpenSearch、PostgreSQL+pgvector与Weaviate,其中Weaviate原生混合搜索质量最佳且开发成本最低,适合以搜索为核心的应用。

2026年6月9日
Weaviate vs. OpenSearch vs. pgvector:向量搜索数据库对比

当应用程序需要搜索功能时,显而易见的候选方案可能是你已经在运行的工具,比如用于全文搜索的 OpenSearch,或用于已构建在关系型数据之上的应用的 PostgreSQL 配合 pgvector 扩展。两者都能存储和查询向量,但它们最初都不是以向量作为核心数据结构来设计的。OpenSearch 是在倒排索引引擎之上以插件形式加入的向量搜索。pgvector 将最近邻搜索实现为一个 SQL 操作符,当向量只是众多列中的一列时这很有用,但它并不适合将语义搜索置于产品核心的场景。

Weaviate 采取了相反的立场。HNSW(分层可导航小世界)向量索引是其核心数据结构,而 BM25 关键词搜索和混合搜索是构建在此基础之上,而非事后添加的。实际效果是,语义搜索、关键词搜索以及两者的混合各自都只需要一次 API 调用。

为了让对比更具参考性,本文将同一组播客转录语料加载到三种数据库中,并从真正能区分它们用于搜索的维度进行比较。这些维度包括:结果质量(向量检索 vs 关键词检索 vs 混合检索)、驻留在应用程序中的搜索逻辑量、数据摄入速度以及索引大小。原始查询速度不是重点——在这个规模下,三者响应速度都足够快,速度不是决定性因素。这次的对比,我们都会直接使用 DigitalOcean 的 OpenSearch、PostgreSQL、Weaviate三个托管数据库进行对比。

DigitalOcean 的Weaviate托管数据库 能为你完成集群的配置、备份和补丁更新,让你无需承担运维开销就能获得这些能力。更多信息可咨询卓普云

先分享些数据结论

  • 混合搜索在每个引擎上都胜出,但只有 Weaviate 能通过一次 API 调用完成。 结合向量检索和关键词检索在所有三个系统中都优于单独使用任何一种。Weaviate 的混合搜索得分最高(hit@10 0.725,其次是 OpenSearch 的 0.685 和 Postgres 的 0.665)。在 OpenSearch 和 pgvector 上,混合搜索需要两次独立查询外加一个你需要编写和维护的排名融合步骤。
  • 向量搜索质量由嵌入决定,而非数据库。 在候选列表深度保持一致的情况下,三个引擎对相同向量的 hit@10 都在 0.575–0.600 范围内。
  • 各引擎的操作性优势各不相同。 Weaviate 的摄入速度最快,因为其 HNSW 和 BM25 索引在一次传输中一同构建;pgvector 最为紧凑(索引 1.87 GB,而 OpenSearch 为 2.91 GB),但需要精心构造查询语句才能让关键词搜索发挥性能。

对比方法

数据集方面,我们使用了播客剧集转录语料,涵盖 4,886 个独立剧集,切分为约 500 个 token 的段落,生成了 100,000 个文档,每个文档包含段落文本及剧集元数据。嵌入向量使用 OpenAI 的 text-embedding-3-small(1,536 维)一次性生成并缓存,因此嵌入步骤对三个系统完全相同。

三个系统均作为 DigitalOcean 托管数据库 运行,通过 DigitalOcean Droplet云服务器 进行查询。

服务引擎 / 索引
Managed WeaviateHNSW(向量)+ BM25(关键词),原生混合搜索
Managed OpenSearchk-最近邻(k-NN)Lucene HNSW + 倒排索引
Managed PostgreSQL + pgvectorHNSW(vector_cosine_ops)+ GIN tsvector

上表和全文会反复出现两种索引。HNSW(分层可导航小世界)是一种基于图的向量搜索索引。它将每个嵌入向量与其近邻连接成分层图,因此引擎通过在图中跳跃而非对比全部 100,000 个向量来找到最接近查询的向量——这使得它快速但近似。倒排索引是经典的关键词搜索结构。它将每个单词映射到包含该词的段落列表,类似于书后的索引将术语映射到页码;BM25 是用于对这些匹配按词的稀有度和在段落内的集中程度进行排序的标准公式。Postgres 使用 pgvector 的 HNSW 实现处理向量,使用 GIN(通用倒排索引)配合 tsvector(其原生全文类型)处理关键词,完成同样的两个角色。

这不是一个受控的硬件基准测试。各实例规格并不匹配,因此本文刻意避免正面速度比较。重点在于搜索结果质量以及独立于硬件存在的工程差异——即各个系统如何进行检索,以及你需要编写多少搜索逻辑。

为了衡量检索质量,我们从数据集中随机抽取了 200 个段落,让一个 LLM(大语言模型)针对每个段落生成一个自然语言问题。然后我们用这些生成的问题作为搜索查询,观察产生该问题的源段落是否出现在前十名结果中。命中(hit)意味着源段落出现在前十名结果中(hit@10),同时我们也记录了其平均倒数排名(MRR),该指标越靠近列表顶部分数越高。Recall@10 是对同一组向量与精确暴力最近邻搜索对比测量的。索引大小则是完整加载后的磁盘占用空间。

索引配置方面,Weaviate 和 pgvector 使用 HNSW 的默认参数,而 OpenSearch 使用 k-NN 插件配合 Lucene HNSW 引擎和余弦相似度。向量是预先计算好的,在三个系统中完全一致。为了使引擎之间具有可比性,三个系统都按照从业者为英文文本配置的方式进行了配置。Weaviate 的关键词操作符严格来说是 BM25F,它可以对多个字段进行加权,但搜索单个内容字段时它退化为标准 BM25——与 OpenSearch 使用的算法相同。OpenSearch 使用 english 分析器(停用词移除和词干提取,与 Postgres 的英文配置和 Weaviate 的分词逻辑相匹配),并在加载后按照 k-NN 插件的建议进行了强制合并。HNSW 的候选列表深度(ef_search)在三个引擎中统一设置为 100,这也是 Weaviate 的默认值。

数据摄入与加载

嵌入向量是预计算的,因此加载操作在三个系统中完成相同的工作:插入 100,000 个段落,并在此基础上构建向量索引和关键词索引。由于各实例规格不匹配,具体时间无法直接迁移到你的硬件上,但速度排序(Weaviate 最快,然后是 Postgres,最后是 OpenSearch)取决于各引擎构建索引的方式——而这部分是可以迁移的。

  • Weaviate 在一次传输中同时完成摄入和索引。当每个对象通过其 gRPC 批处理端点到达时,HNSW 图节点和 BM25 倒排索引条目同时创建,因此加载完成后集合即可完全搜索。无需单独运行、调度或等待索引构建步骤。
  • Postgres 将加载与索引分离。批量插入(execute_values)能快速写入行数据,然后 HNSW 和 GIN 索引作为显式的后续步骤构建。在这个小段落语料上,索引构建很快。在更大的语料上,HNSW 构建会成为主要成本,而且这是你需要自己负责的步骤——你需要安排它、监控它,并在每次重新加载时记得执行它。
  • OpenSearch 在加载过程中反复支付索引成本。其 k-NN 结构在摄入过程中随着段合并而不断重建,因此索引维护与批量加载交织在一起,并在整个过程中不断重新付出成本——这使得它是三者中最慢的。调优(更大的段、延迟刷新)可以挽回部分时间,但这又是驻留在你这一侧的逻辑。

关键的模式是:差异不在于原始的插入速度,而在于索引构建工作发生在哪里——是内联且不可见的(Weaviate)、作为加载后显式步骤(Postgres)、还是在整个加载过程中摊销(OpenSearch)。

混合搜索:一次调用 vs 两次调用

混合搜索结合了语义(向量)和关键词(BM25)相关性,既是质量最高的方法(正如质量部分所展示的),也是这些系统之间架构差异最明显的地方。真正的成本在于你的应用程序需要承担多少。

Weaviate 原生在一次调用中完成混合搜索。它同时查询 HNSW 和 BM25 索引,使用倒数排名融合(RRF)进行融合,并返回一个排序后的结果列表。alpha 参数控制混合比例(0 = 纯关键词,1 = 纯向量)。

results = collection.query.hybrid(
    query=query_text,
    vector=query_vector,
    alpha=0.5,
    limit=10,
)

OpenSearch 在此配置中没有原生混合查询。你需要发出一个向量查询和一个关键词查询,然后在你的应用中融合排序后的结果列表。

knn = client.search(index="passages", body={
    "size": 50, "query": {"knn": {"embedding": {
        "vector": query_vector, "k": 50,
        "method_parameters": {"ef_search": 100}}}}})
bm25 = client.search(index="passages", body={
    "size": 50, "query": {"match": {"transcript_text": query_text}}})

def rrf(*result_lists, k=60):            # 这段代码需要你编写、测试和维护
    scores = {}
    for results in result_lists:
        for rank, hit in enumerate(results):
            scores[hit["_id"]] = scores.get(hit["_id"], 0) + 1 / (k + rank + 1)
    return sorted(scores, key=scores.get, reverse=True)[:10]

results = rrf(knn["hits"]["hits"], bm25["hits"]["hits"])

pgvector 也没有混合搜索的原语,因此同样的两路排名融合需要用 SQL 来表达。

WITH v AS (SELECT guid, row_number() OVER (ORDER BY embedding <=> %(qv)s) rk
           FROM passages ORDER BY embedding <=> %(qv)s LIMIT 50),
     k AS (SELECT guid, row_number() OVER (ORDER BY ts_rank(tsv, to_tsquery('english', %(q)s)) DESC) rk
           FROM passages WHERE tsv @@ to_tsquery('english', %(q)s) LIMIT 50)
SELECT p.guid,
       COALESCE(1.0/(60+v.rk),0) + COALESCE(1.0/(60+k.rk),0) AS rrf_score
FROM passages p LEFT JOIN v USING (guid) LEFT JOIN k USING (guid)
WHERE v.guid IS NOT NULL OR k.guid IS NOT NULL
ORDER BY rrf_score DESC LIMIT 10;
系统混合搜索方式网络往返次数你需要维护的融合逻辑
Weaviate原生,一次调用一次无(仅需 alpha 参数)
OpenSearch两次查询 + 客户端合并两次应用代码中的 RRF 函数
pgvector两次排名 + 在 SQL 中合并一次每个查询中的 RRF 表达式

除了网络往返之外,两路排名方法还带来了维护面。融合逻辑驻留在你的代码中,需要测试,并且必须在每个服务和每种查询搜索的语言中保持一致。使用 Weaviate,调整混合比例只是一个浮点参数。在其他所有系统中,它是一个位于你拥有的函数内部的平滑常数。

搜索质量:向量 vs 关键词 vs 混合搜索

使用方法论中描述的 200 个生成问题,每个引擎根据源段落是否出现在其前十名结果中(hit@10)以及平均倒数排名(MRR)进行评分。

引擎向量(hit@10 / MRR)关键词混合搜索
pgvector0.575 / 0.3760.550 / 0.3570.665 / 0.446
Weaviate0.600 / 0.3990.660 / 0.4650.725 / 0.504
OpenSearch0.580 / 0.3820.635 / 0.4750.685 / 0.450

三个发现值得注意。

  • 混合搜索在 hit@10 上对所有引擎都超过了纯向量和纯关键词搜索。 这是最具操作指导意义的结论。如果搜索质量很重要,请融合两种模态。
  • 向量搜索质量在三个引擎上几乎相同(hit@10 0.575–0.600),因为它们持有相同的嵌入向量,并且在候选深度一致的情况下,检索召回率也几乎相等(0.967–0.988)。数据库不会让向量变得更好或更差——它只能成功或失败地获取这些向量。
  • Weaviate 的混合搜索表现最好,整体结果也最佳(hit@10 0.725,MRR 0.504),这反映了其强大的原生融合能力和 BM25 实现。

Postgres 全文搜索在使用 plainto_tsquery 时得分仅为 0.04,因为该函数要求问题中的每个词都必须匹配。将查询改写为 OR 其词项(使用 to_tsquery 配合 |)后,得分提升至 0.550。OpenSearch 和 Weaviate 的默认行为已经表现良好,但使用 Postgres 时你必须精心构造文本查询。

一个查询的实际结果示例

聚合分数是抽象的,下面展示这些系统对一个真实问题"如何处理销售中的拒绝"实际返回的结果。段落是对话过程中的转录片段,因此它们从句子中间开始。

向量搜索在三个引擎上返回了完全相同的 top 3,因为它们持有相同的嵌入向量。最强有力的答案来自对 Daniel Pink 的采访,内容关于他的书籍《To Sell Is Human》:

"……你肯定会被拒绝,这毫无疑问。所以我谈到的一个特质叫做浮力……每天我都要面对一片拒绝的海洋。好吧,这就是销售的样子……所以浮力就是如何在拒绝的海洋中保持漂浮?"

你选择的数据库不会改变语义搜索发现的内容——嵌入模型才决定这一点。数据库只影响它能够多可靠地获取正确的向量(召回率)。

对于这个查询,关键词搜索在顶部结果上保持一致,三个引擎都将相同的两个段落排在第一位:一位销售老手关于如何开始上门陌生拜访的故事,以及 Art of Charm(Jordan Harbinger)一位听众关于陌生拜访焦虑的问题。差异出现在第三位,每个引擎填充了这个位置的不同结果,不过所有结果都保持在同一主题上。

引擎第三位关键词结果
Weaviate上面提到的 Daniel Pink 浮力段落切题 ✓
pgvector30 Minutes to President's Club,一个 SDR(销售开发代表)陌生拜访培训故事切题 ✓
OpenSearch关于因害怕被拒绝而避免新工作的单集节目切题 ✓

这种差异并不总是这么温和。在另一个评估问题"如果演讲者的观众开始离场,演讲者应该怎么做?"上,只有 Weaviate 返回了生成问题所用的源段落,而其他两个引擎一致地错过了。

引擎第一位关键词结果
WeaviateThe Art of Charm,一位演讲教练精确处理了这个问题。"……如果大家都在离开,我可能会对组织者说……我们需要重新安排我的场次吗?我们需要做些什么来处理这个问题?……"源段落 ✓
pgvector一位音乐人讨论何时停止演奏一首观众不买账的歌曲错误领域 ✗
OpenSearch同一段音乐人内容错误领域 ✗

决定关键词搜索能否保持主题相关的,是字段权重和分析器选择,而非 BM25 算法本身。关键词搜索只是对词进行计数,而非理解它们,因此你让它计数什么就很重要。混合搜索融合了两种信号,对于这个查询,它让三个引擎都落在同样的 top 3 上——销售老手的陌生拜访故事、Daniel Pink 的浮力段落以及陌生拜访焦虑的问题。这正是聚合表所展示的模式。向量搜索在各引擎间保持一致,关键词搜索是分歧点,而混合搜索是实际查询中最可靠的方法。

索引大小

加载全部 100,000 个文档及其 1,536 维嵌入向量、段落文本和元数据后的磁盘占用:

系统索引大小说明
PostgreSQL + pgvector1.87 GB781 MB HNSW + 42 MB GIN 全文索引 + 表数据
OpenSearch2.91 GBk-NN 图 + 倒排索引 + _source 中存储的原始向量
Weaviate未公开Managed Weaviate 未通过 API 暴露磁盘占用

pgvector 最为紧凑,当向量只是现有表中的一列时,这是一个真正的优势。OpenSearch 更大的占用空间部分源于在每个文档的 _source 中保留了原始嵌入数组(与 k-NN 图并存)。(Weaviate 维护了一个 HNSW 图和一个倒排索引,但托管服务目前不公开磁盘占用数据,因此此处不做估计而直接省略。)

何时使用哪个

Weaviate 是在语义搜索或检索增强生成(RAG)为主要工作负载时的正确选择。原生混合搜索、最高的摄入吞吐量和本次对比中最佳的检索质量,使其在搜索为产品核心时成为维护成本最低的选项。一个 alpha 参数取代了整个融合函数及其周围的应用程序逻辑。DigitalOcean Managed Weaviate 数分钟内即可完成配置,无需在代码中进行任何索引管理。

OpenSearch 是在你已经运行 OpenSearch 集群用于全文搜索或日志分析,并希望增量式地添加向量搜索时的正确选择。或者当你需要其成熟的分面搜索(faceting)、聚合和水平扩展能力时也是如此。它的表现令人满意,但混合搜索需要在客户端进行编排,且其 k-NN 引擎选择会显著影响召回率和调优,因此请尽早做出正确的决策。

PostgreSQL + pgvector 是在向量相对于关系型模式处于次要地位时的正确选择——例如在一个事务中,ORDER BY embedding <=> $1 只是众多 WHERE 子句和 JOIN 条件之一。它是本对比中最紧凑的选项,并且能让向量与其描述的数据保持一致。其权衡在于:关键词搜索需要精心构造查询语句,而混合融合需要你自己用 SQL 来表达。

结论

三个系统都能胜任向量的存储和查询,在这个规模下它们的响应速度大致相同。真正有意义的差异体现在返回内容的质量以及你需要构建多少搜索逻辑上。混合检索是全方位最高质量的方法;pgvector 需要精心构造查询才能让关键词搜索正常工作;OpenSearch 需要精心配置分析器和召回率才能具备竞争力;而 Weaviate 以最少的代码和最佳的默认值提供了顶级质量。

对于搜索只是关系型数据旁边的一个功能的应用程序,pgvector 避免了引入新的服务,并将向量保持在其所描述的数据附近——代价是需要自己构建关键词和混合搜索行为。对于搜索本身就是产品的应用程序,在 OpenSearch 或 pgvector 上实现混合搜索的运维开销(两条查询路径、一个融合函数、每个客户端上一致的调优)会不断累积,而 Weaviate 用一个调用和一个参数取代了所有这些,同时提供了本次对比中最佳的检索质量。


相关链接

首页/教程/Weaviate vs. OpenSearch vs. pgvector:向量搜索数据库对比

相关文章

节省 70% 流量费:如何在 DigitalOcean 上构建百万级 QPS 的 ADX 程序化广告架构?
教程

节省 70% 流量费:如何在 DigitalOcean 上构建百万级 QPS 的 ADX 程序化广告架构?

本文深度解析百万级 QPS 下 ADX 系统的四层架构,对比 AWS 痛点,阐述如何利用 DigitalOcean 的超低流量费、高扩展性负载均衡及托管 Kafka 打造高可用、极速响应且低边际成本的程序化广告实时竞价网络。

2026年6月12日
微调后的 LLM 如何部署到生产环境?GPU 推理端点的搭建、测试与上线全流程
精选
教程

微调后的 LLM 如何部署到生产环境?GPU 推理端点的搭建、测试与上线全流程

学会用自有权重搭建私有 GPU 推理端点,从微调、导入到 VPC 内测试和监控,完成模型生产上线全流程。

2026年6月11日
告别 Token 计费时代:Kimi K2.6 与智能体 AI 的预算新范式
教程

告别 Token 计费时代:Kimi K2.6 与智能体 AI 的预算新范式

当智能体自行决定调用多少次模型,你的预算模型还停留在聊天时代吗?Kimi K2.6 + 无服务器推理,给出新解法。

2026年6月9日