Elasticsearch BBQ与OpenSearch FAISS:向量搜索性能对比
基于二进制量化的向量搜索:Elasticsearch使用BBQ技术比OpenSearch搭配FAISS快5倍。我们收到社区要求,希望能解释Elasticsearch与OpenSearch在语义搜索/向量搜索方面的性能差异。因此,我们进行了一系列性能测试,以提供清晰、数据驱动的比较。
二进制量化对决
存储高维向量的原始形式可能会占用大量内存。量化技术能够将这些向量压缩成紧凑的表示形式,显著减少内存占用。搜索过程在压缩空间中进行,这减少了计算复杂度,尤其是在处理大型数据集时,提高了搜索速度。
Elastic致力于使Lucene成为顶级的向量引擎。我们在Elasticsearch 8.16中引入了Better Binary Quantization(BBQ),并在8.18和9.0版本中进一步优化。BBQ基于一种新的标量量化方法,将float32维度缩减至比特,提供约95%的内存减少,同时保持高质量的排名。
另一方面,OpenSearch使用多个向量引擎:nmslib(现已弃用)、Lucene和FAISS。在之前的博客中,我们比较了Elasticsearch与OpenSearch的向量搜索性能。我们使用三个不同的数据集,在两种产品上测试了不同引擎和配置的组合。
本文主要关注两种产品中现有的二进制量化算法。我们使用openai_vector Rally track测试了Elasticsearch搭配BBQ与OpenSearch搭配FAISS的二进制量化。
主要目标是评估两种解决方案在相同召回水平下的性能。什么是召回?召回是衡量搜索系统成功检索到相关结果的比例的一项指标。
在此评估中,召回@k尤其重要,其中k代表考虑的顶级结果数量。因此,召回@10、召回@50和召回@100分别衡量在检索的前10、50和100个项目中出现多少真正相关的结果。召回的表达范围从0到1(或0%到100%的精确度)。这是重要的,因为我们讨论的是近似KNN(ANN)而不是精确KNN,其中召回总是1(100%)。
对于每个k值,我们还指定了n,即在应用最终排名之前考虑的候选项数量。这意味着对于召回@10、召回@50和召回@100,系统首先使用二进制量化算法检索n个候选项,然后对其进行排名,以确定顶级k结果是否包含预期的相关项目。
通过控制n,我们可以分析效率与准确性之间的权衡。较高的n通常增加召回率,因为有更多的候选项可用于排名,但也增加延迟并减少吞吐量。相反,较低的n可以加快检索速度,但如果初始集合中包含的相关候选项太少,可能会降低召回率。
在这次比较中,Elasticsearch在相同的设置下表现出更低的延迟和更高的吞吐量。
方法
完整的配置以及Terraform脚本、Kubernetes清单和具体的Rally track都可以在这个仓库中的openai_vector_bq找到。
与之前的基准测试一样,我们使用了一个Kubernetes集群,该集群由以下节点池组成:
- 1个用于Elasticsearch 9.0的节点池,包含3台
e2-standard-32
机器(128GB RAM和32个CPU) - 1个用于OpenSearch 2.19的节点池,包含3台
e2-standard-32
机器(128GB RAM和32个CPU) - 1个用于Rally的节点池,包含2台
e2-standard-4
机器(16GB RAM和4个CPU)
我们分别设置了一个Elasticsearch 9.0集群和一个OpenSearch 2.19集群。
两者均使用相同的设置进行测试:我们使用openai_vector Rally track,并进行了一些修改——该track使用了来自NQ数据集的250万份文档,并使用OpenAI的text-embedding-ada-002模型生成的嵌入进行了丰富。
代码语言:javascript代码运行次数:0运行复制{ "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:10、n:2000和rescore:2000将检索顶级k(10)个候选项(2000),在2000个结果上应用重排序(相当于“过采样因子”为1)。每次搜索运行10000次,前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
召回@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: v1kind: ConfigMapmetadata: name: rally-params-os labels: app: rally-opensearchdata: 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-bit量化通过将压缩级别设置为“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: v1kind: ConfigMapmetadata: name: rally-params-es labels: app: rally-elasticsearchdata: 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倍。
召回@10 - 详细
任务 | 平均延迟 | 平均吞吐量 | 平均召回 | ||
---|---|---|---|---|---|
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 | 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倍。
详细结果 - 召回@50
任务 | 平均延迟 | 平均吞吐量 | 平均召回 | |
---|---|---|---|---|
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-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的改进
自从BBQ首次发布以来,它取得了显著的进步。在Elasticsearch 8.16中,为了进行比较,我们包括了8.16的基准运行结果,并可以看到召回率和延迟的改善。
在Elasticsearch 8.18和9.0中,我们重写了量化向量的核心算法。因此,虽然8.16中的BBQ表现良好,但最新版本更为出色。您可以在这里和这里阅读相关内容。简而言之,每个向量通过优化的标量分位数单独量化。结果是,用户在不影响性能的情况下,享受更高的向量搜索准确性,使Elasticsearch的向量检索更加强大。
结论
在Elasticsearch BBQ与OpenSearch FAISS的性能比较中,Elasticsearch在向量搜索方面显著优于OpenSearch,在各种召回水平下,查询速度最多快5倍,平均吞吐量高3.9倍。
主要发现包括:
- 召回@10:Elasticsearch BBQ速度最多快5倍(平均快3.9倍),平均吞吐量比OpenSearch FAISS高3.2倍。
- 召回@50:Elasticsearch BBQ速度最多快5倍(平均快4.2倍),平均吞吐量比OpenSearch FAISS高3.9倍。
- 召回@100:Elasticsearch BBQ速度最多快5倍(平均快4.6倍),平均吞吐量比OpenSearch FAISS高3.9倍。
这些结果突出了Elasticsearch BBQ在高维向量搜索场景中的效率和性能优势。Elasticsearch 8.16中引入的Better Binary Quantization(BBQ)技术提供了显著的内存减少(约95%),同时保持高质量的排名,使其成为大规模向量搜索应用的最佳选择。
在Elastic,我们不断创新,以改进Apache Lucene和Elasticsearch,为搜索和检索用例(包括RAG,检索增强生成)提供最佳的向量数据库。我们的最新进展显著提升了性能,使向量搜索更快、更高效。这篇博客是这一创新的又一例证。