微信扫码
添加专属顾问
我要投稿
Uber如何通过OpenSearch突破十亿级向量搜索瓶颈,实现更智能的语义匹配。核心内容: 1. 传统Lucene方案在语义搜索中的局限性 2. OpenSearch的三大核心优势:算法灵活性、GPU加速潜力、可扩展性 3. 15亿条记录的实际应用案例验证
在 Uber,我们的系统每天处理海量数据,涵盖从拼车到外卖的各个环节。我们传统上使用基于关键词的 Apache Lucene ^™^搜索。然而,我们需要超越简单的关键词匹配,转向语义搜索,才能理解搜索背后的含义。
为了实现这一目标,我们采用了 Amazon® ^OpenSearch^作为向量搜索引擎。其可扩展性、性能和灵活性是我们做出这一决定的关键因素。这篇博文将探讨我们评估和实施 OpenSearch 以进行大规模向量搜索的历程,重点介绍我们遇到的基础设施挑战和解决方案。
我们的语义搜索基础设施最初基于 Apache Lucene 及其 HNSW 算法。起初,我们对将机器学习模型中的向量嵌入融入语义检索的前景感到兴奋。早期原型已经证明,这种方法能够显著提升用户体验。
然而,随着应用场景的扩展和数据量的增长,我们在使用 Lucene 的 HNSW 方法时遇到了诸多障碍。算法选项的匮乏限制了我们针对不同场景进行精细化权衡的能力。这意味着我们无法始终为用户提供最准确的结果或最具成本效益的方案。此外,在处理诸如个性化推荐和欺诈检测等复杂任务所需的高维向量时,缺乏原生 GPU 支持也成为了性能瓶颈。这导致响应速度变慢,并限制了我们可部署的机器学习模型的潜在功能。
为了克服这些挑战,我们评估了其他解决方案,包括 Milvus 和 OpenSearch。OpenSearch 很快脱颖而出,成为理想的平台。与 Lucene 不同,OpenSearch 提供了多种人工神经网络 (ANN) 算法,使我们能够针对每个用例选择最佳方案。以下几个因素促成了我们的这一决定:
我们选择采用 OpenSearch 不仅仅是为了选择最符合我们需求的技术,它也标志着我们与亚马逊建立战略合作伙伴关系,并加深与开源社区的合作。
为了真正展现 OpenSearch 在向量搜索方面的强大功能,我们将深入探讨我们在 2024 年开发的一个大规模原型。该原型旨在解决在包含超过 15 亿条记录的庞大数据集中进行搜索的难题,使用户能够在 Uber 生态系统中快速轻松地找到所需内容。该数据集中的每个文档都代表一个包含丰富信息(例如名称和价格)的记录。我们为每个文档嵌入了一个近 400 维的向量,以实现语义检索。
我们主要依赖批量数据导入来向 OpenSearch 提供数据。这个原型系统的工作流程相当简单:
支持如此大规模的数据处理并确保低延迟并非易事。我们必须克服与数据摄取速度、查询性能以及摄取过程中的性能稳定性相关的诸多挑战。
构建十亿级矢量数据索引是一项计算密集型且耗时的任务。在默认设置下,我们无法有效利用计算和 I/O 资源。这导致构建基础索引耗时超过 10 小时。这种低效可能会严重阻碍业务发展,并减慢向量搜索驱动型应用程序的迭代速度。
我们使用默认设置设置了基线,导入整个数据集耗时 12.5 小时。
我们注意到,基准索引作业对可用的 CPU 资源利用不足,通常只使用了不到一半的分配容量,如图 3 所示。鉴于构建 HNSW 向量搜索索引是一项 CPU 密集型任务,提高 CPU 利用率有望显著提升索引过程的性能。
如图 4 所示,为了提高 CPU 利用率,我们增加了索引作业的吞吐量。我们开发了一个基于 Spark 的批量数据导入工具,该工具由开源的 Spark OpenSearch 连接器[1]提供支持。为了提升索引吞吐量,我们增加了 Spark 核心数、执行器数和分区数。此外,我们还增加了用于原生 KNN 索引创建的线程数(knn.algo_param.index_thread_qty[2])。
如图 5 所示,指标表明基线索引过程导致了过多的读/写 I/O。这种高 I/O 使用率很可能导致文档索引延迟。总 I/O 量甚至超过了索引本身的大小,表明存在不必要的冗余写入。过高的读取 I/O 还表明后台频繁进行段压缩。这些问题很可能是由于索引过程中生成了大量小段造成的。频繁的刷新和清除操作,加上不合理的合并策略,很可能是造成这些小段数量激增的原因。
为了减轻磁盘 I/O 放大带来的影响,我们采取了多项措施来减少不必要的磁盘读取 I/O,如图 6 所示。首先,我们减少了刷新操作。我们禁用了默认的 1 秒刷新间隔,仅在每次索引请求时进行刷新(index.refresh_interval:-1)。然后,我们更新了刷新策略以减少刷新操作。我们将 flush_threshold_size_从_518 MB 增加到 1024 MB。我们还将刷新行为从每次索引请求刷新改为每 120 秒同步一次。为了更新合并策略,我们将初始段大小从 1MB 增加到 10MB,从而减少了小段文件。我们还通过将_segments_per_tier_从 10 增加到 15 以及_max_merge_at_once_从 10 增加到 15 来降低合并频率。这降低了触发合并操作的概率。
我们在索引设置中禁用了__source_和_doc_values ,将索引大小从 11 TB 减少到 4 TB,从而减轻了 I/O 和磁盘压力。_
在 Uber,我们通常对延迟有较为严格的要求,以确保客户(包括搜索用户)获得流畅的体验。一般来说,我们的目标是在 2K QPS 下实现 100 毫秒的 P99 延迟。为了满足这一要求,我们在可扩展性和各种调优点方面面临着诸多挑战。就可扩展性而言,我们之前从未有过调优 OpenSearch 集群来服务于十亿级 KNN 索引的经验,尤其是在我们的用例对延迟有严格要求的情况下。就各种调优点而言,集群级别和索引级别都有数十种可调配置,这些配置都会影响查询性能。要系统地理解它们的影响方向和规模非常困难,尤其是在它们相互叠加的情况下。
我们最初的设置是在摄取后使用未更改的集群/索引配置,可以以 250 毫秒的 P99 延迟和 2K QPS 提供服务。
当分片数小于集群中的节点数时, 更多的分片通常意味着更高的查询并行度,这通常意味着更低的延迟。当分片数大于节点数时,节点数限制了并行度。此时,聚合来自不同分片的结果所带来的开销可能会超过并行度带来的收益,并对查询性能产生负面影响。
如图 7 所示,对于此特定用例,当分片数等于节点数时,我们观察到最佳性能。
在我们的用例中,扇出查询工作负载分布在所有分片和所有节点上。如果没有副本,集群中最慢的节点始终是延迟的瓶颈。添加副本可以进一步平滑延迟。因此,可以将更多负载分配给性能更好的节点,例如网络状况更佳或邻居节点负载更低的节点。理论上,这可以带来更好的整体性能。
结合基准数据,我们发现了一个明显的趋势:副本数越多,整体查询性能越好,这与我们的理论相符。
K 值越大,后续排序后的整体数据质量就越好,但代价是查询性能下降。我们一直希望获得更高的召回率,但我们需要了解增大 K 值的影响。
我们清楚地看到,K值越高,整体延迟也越高。然而,如图9所示,延迟增加的幅度很小。
只有当图完全加载到内存中时,图遍历才能高效运行。否则,查询时需要大量的磁盘 I/O,这将显著影响性能。有两个变量控制着分配给 KNN 图的内存:数据节点的总内存分配量和 _knn.memory.circuit_breaker.limit_ 配置项,后者控制着在排除 JVM 堆内存后,分配给 KNN 图的内存百分比。
当分配的内存小于 KNN 图的大小时(例如,knn.circuit_breaker.triggered=true),查询时间在几十秒范围内。我们观察到执行查询时存在大量的磁盘 I/O,特别是对于遍历所有分片的扇出查询。
CSS 支持在多个核心上对多个细分市场进行并发搜索。理论上,在某些情况下,它应该可以降低查询延迟。
我们观察到启用 CSS 后 CPU 使用率较高。然而,由于同样的原因,计算资源耗尽得更早,导致饱和 QPS 从 1 万降至 7 千。此外,就此特定案例而言,我们在 2 千 QPS 时并未观察到 P99 延迟的明显降低,这可能是由于搜索空间本身就很小,因此 CSS 的开销可能超过了其带来的收益。我们在其他一些用例中观察到了明显的性能提升,例如:
随着我们基于向量搜索的应用场景迅速扩展,定期创建新的基础索引变得至关重要。然而,为数十亿个向量生成索引需要消耗大量资源,如果在同一集群内构建新索引,则会严重影响搜索性能。
我们通过在独立集群上创建新索引并平滑地重新路由流量,实现了蓝绿部署,从而保证了零停机时间,并将对搜索性能的影响降至最低。图 11 显示了其高层架构。
综上所述,我们的优化工作主要通过改进索引和查询流程显著提升了性能:
这些改进展现了 OpenSearch 高效处理海量向量搜索工作负载的能力,也为未来更具雄心的项目奠定了基础。我们将持续优化系统,随着搜索能力的扩展和发展,不断提升性能,力求达到最佳状态。
我们与 OpenSearch 的合作才刚刚开始。我们很高兴能够继续探索它的各项功能,并进一步改进我们的向量搜索系统。
未来的工作重点之一是利用GPU提升向量搜索性能。借助GPU加速,我们有望显著提升向量引擎的性能,尤其是在数据集持续增长的情况下。这将转化为更快的搜索结果、更灵敏的响应以及更佳的用户体验。OpenSearch社区已提出一项启用GPU加速的计划,Uber也计划为此做出贡献。通过利用GPU的强大功能,我们旨在提升OpenSearch向量引擎的性能,尤其是在处理海量数据集时。
未来工作的另一个方向是将集群内的索引和搜索流量分离,以实现独立的扩展性和故障/工作负载隔离,尤其是在处理大规模数据集时。OpenSearch 社区一直在致力于读写分离。Uber 计划集成此功能,并将蓝绿部署机制升级到这一成熟的解决方案。
最后,我们希望支持实时更新。目前,我们的物品搜索依赖于批量数据摄取来更新向量索引。然而,随着我们向更动态、对时间要求更高的应用场景发展,实时更新变得越来越重要。我们计划利用 Apache Flink® 等流式处理技术来实时摄取数据更新,从而实现这一目标。
[1] 开源的 Spark OpenSearch 连接器: https://github.com/opensearch-project/opensearch-hadoop/tree/4e934f4b4ea6af509fde31c42daa2c4bf84b36b1[2] knn.algo\_param.index\_thread\_qty: https://opensearch.org/docs/latest/search-plugins/knn/settings#cluster-settings
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2025-12-22
别让大模型在“垃圾堆”里找金子:深度解析 RAG 的上下文压缩技术
2025-12-21
终于,NotebookLM 和 Gemini 合体了。这是什么神之更新?
2025-12-21
Cohere 推出 Rerank 4,将上下文窗口从 8K 扩展至 32K,以交叉编码器架构强化长文档语义理解与跨段落关联捕捉
2025-12-21
4.1K Star!GitHub 上挖到一个救星级别的 RAG 数据流水线项目!
2025-12-20
PageIndex:一种基于推理的 RAG 框架
2025-12-20
深度解析丨智能体架构,利用文件系统重塑上下文工程
2025-12-20
RAG 答非所问?可能是你少了这一步:深度解析 Rerank 与 Cross-Encoder 的“降维打击”
2025-12-18
从 RAG 到 Context:2025 年 RAG 技术年终总结
2025-10-11
2025-10-04
2025-09-30
2025-10-12
2025-12-04
2025-11-04
2025-10-31
2025-11-13
2025-10-12
2025-12-03
2025-12-21
2025-12-10
2025-11-23
2025-11-20
2025-11-19
2025-11-04
2025-10-04
2025-09-30