博客

Elasticsearch BBQ 与 OpenSearch FAISS:矢量搜索性能比较

Elasticsearch BBQ 和 OpenSearch FAISS 的性能比较。

二进制量化矢量搜索使用 BBQ 的 Elasticsearch 比使用 FAISS 的 OpenSearch 快 5 倍。Elastic 收到了来自社区的请求,希望澄清 Elasticsearch 和 OpenSearch 之间的性能差异,尤其是在语义搜索/矢量搜索领域,因此我们进行了这些性能测试,以提供清晰的、数据驱动的比较。

Elasticsearch BBQ 与 OpenSearch FAISS - 速度& 吞吐量 召回率比较

二进制量化对决

以原始形式存储高维向量会耗费大量内存。量化技术可将这些矢量压缩为紧凑的表示形式,从而大大减少内存占用。然后,搜索在压缩空间内进行,从而降低了计算复杂度,加快了搜索速度,尤其是在大型数据集中。

Elastic 致力于使 Lucene 成为性能一流的矢量引擎。在 Elasticsearch 8.16 中,我们在 Lucene 的基础上引入了更好的二进制量化(BBQ),并在 8.18 和 9.0 中进一步发展。BBQ 基于一种新的标量量化方法,将 float32 维度减少到比特,在保持高排名质量的同时,减少了 ~95% 内存。

另一方面,OpenSearch 使用多种矢量引擎:nmslib(现已废弃)、Lucene 和 FAISS。在 一篇 博客 中,我们比较了 Elasticsearch 和 OpenSearch 的向量搜索。我们使用了三个不同的数据集,并在两种产品上测试了不同的引擎和配置组合。

本博客重点介绍这两种产品目前提供的二进制量化算法。我们使用 BBQ 对 Elasticsearch 进行了测试,并使用FAISS 的二进制量化openai_vectorRally track)对 OpenSearch 进行了测试。

主要目的是评估两种解决方案在相同召回率下的性能。召回是什么意思?召回率是衡量搜索系统成功检索到多少相关结果的指标。

在这项评估中,recall@k 尤为重要,其中k代表所考虑的顶级结果的数量。因此,Recall@10Recall@50 和 Recall@100分别用来衡量有多少真正相关的结果出现在检索结果的前 10、50 和 100 项中。召回率以 0 到 1 的范围表示(或 0% 到 100% 精确度)。这一点很重要,因为我们讨论的是近似 KNN (ANN),而不是精确 KNN,后者的召回率总是 1 (100%).

对于每个k值,我们还指定了n, 即在应用最终排名之前考虑的候选者数量。这意味着,对于 Recall@10、Recall@50 和 Recall@100,系统首先使用二进制量化算法检索n 个候选项,然后对它们进行排序,以确定前k 个结果是否包含预期的相关项。

通过控制n,我们可以分析效率和精度之间的权衡。较高的n通常会增加召回率,因为有更多候选者可供排序,但同时也会增加延迟和 降低 吞吐量。相反,n越小,检索速度越快,但如果初始集合中包含的相关候选信息太少,则可能会降低召回率。

在这次比较中,Elasticsearch 在相同的设置下比 OpenSearch 的延迟更低,吞吐量更高。

方法

完整的配置,以及 Terraform 脚本、Kubernetes 清单和特定的 Rally 轨道,都可在此仓库 openai_vector_bq 下找到。

与之前的基准测试一样,我们使用的 Kubernetes 集群由以下部分组成:

  • 1 个用于 Elasticsearch 9.0 的节点池,包含 3 台e2-standard-32 机器(128GB 内存和 32 个 CPU)

  • 1 个用于 OpenSearch 2.19 的节点池,包含 3 台e2-standard-32 机器(128GB 内存和 32 个 CPU)

  • 1 个用于 Rally 的节点池,包含 2 台e2-standard-4 机器(16GB 内存和 4 个 CPU)

Elasticsearch BBQ 与 Opensearch FAISS 方法论设置对比

我们建立了一个版本为 9.0 的 Elasticsearch 集群和一个版本为 2.19 的 OpenSearch 集群。

Elasticsearch 和 OpenSearch 都使用了完全相同的设置进行测试:我们使用了经过 一些修改的 openai_vector Rally track,它使用了来自 NQ 数据集 的 250 万份文档,并使用 OpenAI 的 text-embedding-ada-002 模型 生成了丰富的嵌入。

{
  "source-file": "open_ai_corpus-initial-indexing.json.bz2",
  "document-count": 2580961,
  "compressed-bytes": 32076749416,
  "uncompressed-bytes": 90263571686
}

结果报告了在不同召回级别(召回率@10、召回率@50 和召回率@100)下,使用 8 个客户端同时执行搜索操作所测出的延迟和吞吐量。我们只使用一个分片,没有副本。

我们运行了以下 k-n-rescore 组合,例如10-2000-2000,或k:10n:2000rescore:2000将检索 n 个候选者(2000)中的前 k(10),并对 2000 个结果进行重新评分(相当于 "超抽样因子 "1)。每次搜索运行 10.000 次,预热 1000 次:

召回@10

  • 10-40-40

  • 10-50-50

  • 10-100-100

  • 10-200-200

  • 10-500-500

  • 10-750-750

  • 10-1000-1000

  • 10-1500-1500

  • 10-2000-2000

回忆@50

  • 50-150-150

  • 50-200-200

  • 50-250-250

  • 50-500-500

  • 50-750-750

  • 50-1000-1000

  • 50-1200-1200

  • 50-1500-1500

  • 50-2000-2000

Recall@100

  • 100-200-200

  • 100-250-250

  • 100-300-300

  • 100-500-500

  • 100-750-750

  • 100-1000-1000

  • 100-1200-1200

  • 100-1500-1500

  • 100-2000-2000

为了复制基准,Rally-elasticsearch 和 rally-opensearch 的 Kubernetes 清单都将所有相关变量外部化到了 ConfigMap 中,可在此处(ES)和此处(OS)查看。可以自定义search_ops参数,以测试 k、n 和 rescore 的任意组合。

OpenSearch Rally 配置

/k8s/rally-openai_vector-os-bq.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-os
  labels:
    app: rally-opensearch
data:
  user-tags.json: |
    {
      "product": "OpenSearch",
      "product-version": "OpenSearch-2.19.0",
      "product-label": "OpenSearch-2.19-faiss",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "ann_threshold": 0,
      "vector_mode": "on_disk",
      "compression_level": "32x",
      "vector_method_name": "hnsw",
      "vector_method_engine": "faiss",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }

Opensearch 索引配置

然后,ConfigMap 中的变量将用于索引配置,某些参数则保持不变。OpenSearch 中的 1 位量化是通过 将压缩级别设置为 "32x " 来配置的

index-vectors-only-mapping-with-docid-mapping.json

{
  "settings": {
    {% if preload_pagecache %}
    "index.store.preload": [
      "vec", "vex", "vem", "veq", "veqm", "veb", "vebm"
    ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }},
    "index.knn": true,
    "index.knn.advanced.approximate_threshold": {{ ann_threshold | default(15000) }}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "knn_vector",
        "dimension": 1536,
        "space_type": "innerproduct",
        "data_type": "float",
        "mode": {{ vector_mode | default("in_memory") | tojson }},
        "compression_level": {{ compression_level | default("32x") | tojson }},
        "method": {
          "name": {{ vector_method_name | default("hnsw") | tojson }},
          "engine": {{ vector_method_engine | default("faiss") | tojson }},
          "parameters": {
            "ef_construction": 100,
            "m": 16
          }
        }
      }
    }
  }
}

Elasticsearch Rally 配置

/k8s/rally-openai_vector-es-bq.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-es
  labels:
    app: rally-elasticsearch
data:
  user-tags.json: |
    {
      "product": "Elasticsearch",
      "product-version": "Elasticsearch-9.0.0-ade01164",
      "product-label": "Elasticsearch-9.0-BBQ",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "vector_index_type": "bbq_hnsw",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }

Elasticsearch 索引配置

index-vectors-only-mapping-with-docid-mapping.json

{
  "settings": {
    {# non-serverless-index-settings-marker-start #}
    {%- if build_flavor != "serverless" or serverless_operator == true -%}
    {% if preload_pagecache %}
    "index.store.preload": [ "vec", "vex", "vem", "veq", "veqm", "veb", "vebm" ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }}
    {%- endif -%}
    {# non-serverless-index-settings-marker-end #}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "dense_vector",
        "element_type": "float",
        "dims": 1536,
        "index": true,
        "similarity": "dot_product",
        "index_options": {
          "type": {{ vector_index_type | default("bbq_hnsw") | tojson }},
          "ef_construction": 100,
          "m": 16
        }
      }
    }
  }
}

实施结果

对结果有多种解释方法。对于延迟和吞吐量,我们绘制了每个召回级别的简化图和详细图。如果我们考虑到每项指标 "越高越好",就很容易发现其中的差异。不过,延迟是负值(实际上越低越好),而吞吐量是正值。在简化图表中,我们使用(召回率/延迟)*10000 (简称 "速度")和 召回率*吞吐量,因此这两个指标都意味着速度越快和吞吐量越大越好。让我们开始吧。

召回 @ 10 - 简化

在这种召回水平下,Elasticsearch BBQ 比 OpenSearch FAISS快 5 倍 (平均快 3.9 倍),吞吐量平均高出 3.2 倍。

在速度和吞吐量方面,Elasticsearch BBQ 比 OpenSearch FAISS 快达 5 倍(平均快 3.9 倍),吞吐量平均高出 3.2 倍 Recall@10
与 OpenSearch FAISS 相比,Elasticsearch BBQ 的速度快达 5 倍(平均快 3.9 倍),吞吐量平均高出 3.2 倍。

召回 @ 10 - 详细

Elasticsearch BBQ 与 Opensearch FAISS 的详细召回@10 延迟比较
Elasticsearch BBQ 与 Opensearch FAISS 的详细召回率@10 吞吐量比较。

工作

latency.mean

吞吐量平均值

平均调用次数

Elasticsearch-9.0-BBQ

10-100-100

11.70

513.58

0.89

Elasticsearch-9.0-BBQ

10-1000-100

27.33

250.55

0.95

Elasticsearch-9.0-BBQ

10-1500-1500

35.93

197.26

0.95

Elasticsearch-9.0-BBQ

10-200-200

13.33

456.16

0.92

Elasticsearch-9.0-BBQ

10-2000-2000

44.27

161.40

0.95

Elasticsearch-9.0-BBQ

10-40-40

10.97

539.94

0.84

Elasticsearch-9.0-BBQ

10-50-50

11.00

535.73

0.85

Elasticsearch-9.0-BBQ

10-500-500

19.52

341.45

0.93

Elasticsearch-9.0-BBQ

10-750-750

22.94

295.19

0.94

OpenSearch-2.19-faiss

10-100-100

35.59

200.61

0.94

OpenSearch-2.19-faiss

10-1000-1000

156.81

58.30

0.96

OpenSearch-2.19-faiss

10-1500-1500

181.79

42.97

0.96

OpenSearch-2.19-faiss

10-200-200

47.91

155.16

0.95

OpenSearch-2.19-faiss

10-2000-2000

232.14

31.84

0.96

OpenSearch-2.19-faiss

10-40-40

27.55

249.25

0.92

OpenSearch-2.19-faiss

10-50-50

28.78

245.14

0.92

OpenSearch-2.19-faiss

10-500-500

79.44

97.06

0.96

OpenSearch-2.19-faiss

10-750-750

104.19

75.49

0.96

召回 @ 50 - 简化

在这种召回水平下,Elasticsearch BBQ 比 OpenSearch FAISS 快多达 5 倍 (平均快 4.2 倍),平均 吞吐量多 3.9 倍 。

Recal @50 向量性能对比 Elasticsearch BBQ vs Opensearch FAISS
Elasticsearch BBQ 的速度比 OpenSearch FAISS 快 5 倍(平均快 4.2 倍),吞吐量平均高出 3.9 倍。

详细结果 - 召回率 @ 50

Recall@50 Elasticsearch BBQ 和 Opensearch FAISS 延迟结果
Recall@50 Elasticsearch BBQ 和 Opensearch FAISS 吞吐量结果

任务

平均时延

吞吐量平均值

平均召回率

Elasticsearch-9.0-BBQ

50-1000-1000

25.71

246.44

0.95

Elasticsearch-9.0-BBQ

50-1200-1200

28.81

227.85

0.95

Elasticsearch-9.0-BBQ

50-150-150

13.43

362.90

0.90

Elasticsearch-9.0-BBQ

50-1500-1500

33.38

202.37

0.95

Elasticsearch-9.0-BBQ

50-200-200

12.99

406.30

0.91

Elasticsearch-9.0-BBQ

50-2000-2000

42.63

163.68

0.95

Elasticsearch-9.0-BBQ

50-250-250

14.41

373.21

0.92

Elasticsearch-9.0-BBQ

50-500-500

17.15

341.04

0.93

Elasticsearch-9.0-BBQ

50-750-750

31.25

248.60

0.94

OpenSearch-2.19-faiss

50-1000-1000

125.35

62.53

0.96

OpenSearch-2.19-faiss

50-1200-1200

143.87

54.75

0.96

OpenSearch-2.19-faiss

50-150-150

43.64

130.01

0.89

OpenSearch-2.19-faiss

50-1500-1500

169.45

46.35

0.96

OpenSearch-2.19-faiss

50-200-200

48.05

156.07

0.91

OpenSearch-2.19-faiss

50-2000-2000

216.73

36.38

0.96

OpenSearch-2.19-faiss

50-250-250

53.52

142.44

0.93

OpenSearch-2.19-faiss

50-500-500

78.98

97.82

0.95

OpenSearch-2.19-faiss

50-750-750

103.20

75.86

0.96

召回 @ 100

在这种召回水平下,Elasticsearch BBQ 比 OpenSearch FAISS快 5 倍 (平均快 4.6 倍),吞吐量 平均高 3.9 倍。

回忆 @100 Elasticsearch BBQ 与 Opensearch FAISS 的结果
Elasticsearch BBQ 和 Opensearch FAISS 延迟和吞吐量性能比较

详细结果 - Recall @ 100

详细的延迟结果 - Recall @ 100 Elasticsearch BBQ vs Opensearch FAISS
详细的吞吐量结果 - Recall @ 100 Elasticsearch BBQ vs Opensearch FAISS。

工作

latency.mean

吞吐量平均值

平均调用次数

Elasticsearch-9.0-BBQ

100-1000-1000

27.82

243.22

0.95

Elasticsearch-9.0-BBQ

100-1200-1200

31.14

224.04

0.95

Elasticsearch-9.0-BBQ

100-1500-1500

35.98

193.99

0.95

Elasticsearch-9.0-BBQ

100-200-200

14.18

403.86

0.88

Elasticsearch-9.0-BBQ

100-2000-2000

45.36

159.88

0.95

Elasticsearch-9.0-BBQ

100-250-250

14.77

433.06

0.90

Elasticsearch-9.0-BBQ

100-300-300

14.61

375.54

0.91

Elasticsearch-9.0-BBQ

100-500-500

18.88

340.37

0.93

Elasticsearch-9.0-BBQ

100-750-750

23.59

285.79

0.94

OpenSearch-2.19-faiss

100-1000-1000

142.90

58.48

0.95

OpenSearch-2.19-faiss

100-1200-1200

153.03

51.04

0.95

OpenSearch-2.19-faiss

100-1500-1500

181.79

43.20

0.96

OpenSearch-2.19-faiss

100-200-200

50.94

131.62

0.83

OpenSearch-2.19-faiss

100-2000-2000

232.53

33.67

0.96

OpenSearch-2.19-faiss

100-250-250

57.08

131.23

0.87

OpenSearch-2.19-faiss

100-300-300

62.76

120.10

0.89

OpenSearch-2.19-faiss

100-500-500

84.36

91.54

0.93

OpenSearch-2.19-faiss

100-750-750

111.33

69.95

0.94

烧烤炉的改进

自首次发布以来,BBQ 已经取得了长足的进步。在 Elasticsearch 8.16 上,为了便于比较,我们将 8.16 的基准运行与当前的基准运行放在一起,我们可以看到召回率和延迟从那时起有了怎样的改善。

以 Elasticsearch 8.16 BBQ 为基准测试 Elasticsearch 9.0 BBQ 延迟召回改进情况

在 Elasticsearch 8.18 和 9.0 中,我们重写了量化向量的核心算法。因此,8.16 版的 BBQ 已经很不错了,而最新版本则更加出色。您可以在这里这里了解相关信息。简而言之,每个矢量都通过优化的标量量化进行了单独量化。因此,用户可以在不影响性能的情况下获得更高的向量搜索准确性,使 Elasticsearch 的向量检索功能更加强大。

结论

在 Elasticsearch BBQ 和 OpenSearch FAISS 的性能比较中,Elasticsearch 在矢量搜索方面明显优于 OpenSearch,在各种召回级别中,Elasticsearch 的查询速度平均提高了 5 倍,吞吐量提高了 3.9 倍。

主要发现包括

  • Recall@10:与 OpenSearch FAISS 相比,Elasticsearch BBQ 的速度快达 5 倍(平均快 3.9 倍),吞吐量平均高出 3.2 倍。

  • Recall@50:与 OpenSearch FAISS 相比,Elasticsearch BBQ 的速度快达 5 倍(平均快 4.2 倍),吞吐量平均高出 3.9 倍。

  • Recall@100:与 OpenSearch FAISS 相比,Elasticsearch BBQ 的速度快达 5 倍(平均快 4.6 倍),吞吐量平均高出 3.9 倍。

这些结果凸显了 Elasticsearch BBQ 的效率和性能优势,尤其是在高维向量搜索场景中。Elasticsearch 8.16 中引入的更好的二进制量化(BBQ)技术在保持较高排序质量的同时,大幅减少了内存(~95% ),是大规模矢量搜索应用的上佳选择。

在 Elastic,我们坚持不懈地创新,改进 Apache Lucene 和 Elasticsearch,为搜索和检索用例(包括 RAG(检索增强生成))提供最佳的向量数据库。在 Lucene 10 的基础上,我们最近取得的进步大大提高了性能,使矢量搜索比以前更快、更节省空间。本博客就是这种创新的又一例证。

相关内容

LINQ to Elasticsearch ES|QL:编写 C# 代码,查询 Elasticsearch

Florian Bernd

利用 Elasticsearch 中的 NLP 和向量搜索增强聊天机器人功能

Priscilla Parodi

Elasticsearch 与 OpenSearch:向量搜索性能比较

Ugo Sangiorgi

人工智能剽窃利用 Elasticsearch 检测剽窃行为

Priscilla Parodi

高级 RAG 技术第 2 部分:查询和测试

Han Xiang Choong

准备好打造最先进的搜索体验了吗?

足够先进的搜索不是一个人的努力就能实现的。Elasticsearch 由数据科学家、ML 操作员、工程师以及更多和您一样对搜索充满热情的人提供支持。让我们联系起来,共同打造神奇的搜索体验,让您获得想要的结果。

亲自试用