写点什么

优化搜索系统:平衡速度、相关性和可伸缩性

作者:Janani Narayanan、Karthik Ramasamy

  • 2025-07-17
    北京
  • 本文字数:5604 字

    阅读完需:约 18 分钟

大小:2.78M时长:16:11
优化搜索系统:平衡速度、相关性和可伸缩性

作为软件工程师,我们始终致力于构建不仅功能强大,而且高效且可伸缩的系统。在一个用户要求更快、结果更准确的世界里,优化搜索性能已成为现代应用开发的关键焦点。

 

这篇文章基于我们在 2024 年旧金山 QCon 大会上的演讲,我们探讨了数据索引、检索和排名不断发展的格局。像优步外卖(Uber Eats)这样的平台需要在庞大的数据集中处理复杂的查询,优化搜索已成为一项关键挑战,这需要诸如索引、分片和并行查询处理之类的高级策略。

 

搜索系统的复杂性持续增长,使得速度、相关性和可伸缩性之间的平衡比以往任何时候都更加重要。这篇文章探讨了这些优化背后的技术及其对用户体验和系统性能的影响。

 

在优步外卖中扩展选择:nX 方法

在优步外卖(Uber Eats)中,选择是一个复杂的问题,其复杂性因视角的不同而异。对于商家入驻团队和运营部门而言,成功在于尽可能多地引入餐厅和店铺。对于消费者来说,选择可能意味着不同的事情。有些人优先考虑快速的配送时间,而另一些人则在应用程序中寻找他们最喜欢的餐厅或探索新地方的机会。挑战在于满足这些不同的需求,同时确保发现体验的无缝衔接。

 

除了选择的概念性方面,还有重大的技术障碍需要克服。随着业务的扩展,尤其是在疫情期间和之后,Uber Eats 已经将新的垂直领域纳入其中,例如杂货店、零售店,甚至商品/包裹配送。这种扩展给平台的选择基础设施带来了排名和推荐的复杂性。

 

在扩展选择方面,餐馆和杂货店之间的一个关键区别在于入驻。餐厅菜单通常包含二十到三十个菜品,而杂货店可能有超过十万种库存单位(SKU)。这种差异需要一个先进的索引系统来有效地向用户展示相关选项。

 

优步外卖选择策略的另一个重大发展是扩大配送的地理范围。以前,用户只能从距离自己 10 到 15 分钟路程范围内的餐厅下单。而现在,该平台能够支持用户从近一小时路程范围内的餐厅下单。例如,一个城市的用户可以从另一个城市的一家餐厅或零售店下单,并通过优步外卖进行配送。这种范围的扩大带来了更多的技术挑战,尤其是在物流和配送方面。

 

优步外卖的选择策略主要集中在最大限度地增加其应用程序内所有发现界面的可用选项数量。个性化虽然也很重要,但这是一个单独的挑战,需要不同的解决方案。通过不断改进其索引、排名和推荐技术,优步外卖致力于为用户提供更全面、更动态的选择,确保他们能够快速高效地找到自己想要的东西,无论是最喜爱的餐厅、新的美食体验,还是急需的杂货。

 

诸如首页推送(Home Feed)、搜索、推荐和广告之类发现界面在连接用户与可用选项方面至关重要。首页推送是下单的主要入口,其中包含基于用户历史记录的轮播图、店铺列表以及促销和菜系的快捷入口。搜索功能涵盖餐厅、菜品和菜系搜索,而推荐功能则帮助用户动态发现类似或替代选项。广告提升了可见度,帮助商家有效触达相关客户。确保这些发现界面的一致性是提供无缝且直观用户体验的关键。


用户如何在优步外卖上发现内容:首页推荐、搜索和推荐概览

 

优步外卖的架构:从基础设施到应用层

 

优步外卖的架构涵盖多个层级,从基础设施层到应用层,确保餐厅和店铺的检索与定位无缝衔接。其基础是基础设施层,用于存储和索引所有可用的商家和商品。这是检索相关店铺的核心数据集。

 

搜索架构:检索与排名管道的分层视图

 

检索层通过获取大量潜在店铺来优化召回率,然后通过“排名”系统对相关性进行细化。首轮排名侧重于精准度,利用词法匹配将用户查询与检索到的文档进行对齐。水合层添加业务逻辑,考虑促销活动、会员福利和预计送达时间等因素。最后,第二轮排名根据用户的订单历史和转化率对结果进行个性化处理,以提供最相关的选项。

 

随着优步外卖选择范围的扩大,它遇到了显著的伸缩性挑战。最初尝试增加检索店铺的数量导致延迟增加了四倍,这促使对数据摄取、查询性能和排名方面的低效进行更深入的调查。

 

点的大小反映候选店铺的数量;红点表示基于更大的哈弗辛距离而新纳入的店铺。

 

其中一个关键挑战是索引和查询。优步外卖依靠 H3 六边形地理定位索引来确定配送区域,但摄取过程将店铺错误地归类为“近”或“远”。这种错误分类导致了排名不一致的问题,一些实际上距离较近的店铺被降级处理。




搜索的另一个问题是搜索空间呈指数级增长。稍微扩大配送范围就会使搜索区域呈平方增长,大幅增加了需要处理的候选店铺数量。这意味着即使对检索参数进行微小调整,也可能导致性能显著下降。

 


店铺分布也带来了另一个挑战。随着搜索范围的扩大,较远的店铺往往在推荐列表中排名高于附近的店铺。这就造成了一个两难的局面:虽然高转化率的店铺很重要,但当远距离的选择压倒了更方便的本地选项时,客户体验就会受到影响。

 

检索的哈弗辛环:内圈店铺少,外圈增长不成比例

 

为了解决这些问题,优步外卖需要优化其索引、检索和排名策略。重点转向在规模和效率之间取得平衡,确保平台能够展示最相关的店铺,同时不牺牲性能。这些优化措施对于在支持优步外卖不断扩大的商品种类的同时保持流畅的用户体验至关重要。

 

优步外卖的搜索平台

为优步外卖提供支持的搜索平台,每天处理数千万次请求,建立在 Apache Lucene 之上,并采用 Lambda 架构进行数据摄取。该架构包括通过 Spark 进行批量摄取以及通过流处理路径进行实时摄取,以确保搜索结果的时效性。

 

从数据到可发现性:由流处理架构协调的摄取、索引编制和基于 Lucene 的搜索

 

一个关键特性是优先级感知摄取,允许优先级高的请求优先处理,保持数据的新鲜度。优步还大量依赖地理分片来优化其地理空间搜索用例。此外,自定义索引布局和查询操作符通过利用离线文档排名和提前终止来提高搜索效率,从而加快查询速度。

 

搜索平台架构由三个主要组件组成:

 

  • 批处理索引编制管道——Spark 作业处理数据,将它们转换为搜索文档,将它们分区为分片,并生成存储在对象存储中的 Lucene 索引。

  • 流式/实时更新路径——更新通过流处理服务摄取,该服务将文档映射到特定的 Kafka 分区以进行实时更新。Kafka 分区和分片之间存在一对一的映射关系。Kafka 充当预写日志,能够优雅地处理摄入峰值、优先级感知摄入、复制和容错。

  • 服务栈——搜索器节点从存储中检索索引,赶上流式更新,并执行查询。无状态聚合服务将搜索请求路由到适当的搜索器节点,处理查询分发,并在将结果在返回给用户之前进行聚合。

 

分片技术

在优步外卖上高效管理地理空间搜索查询至关重要,因为用户经常寻找附近的餐厅或杂货店。为了实现这一点,优步外卖采用地理分片技术,确保特定位置的所有相关数据都存储在一个分片中。这能减少查询开销,并消除从多个分片获取和聚合结果造成的低效率。此外,地理分片还允许在数据节点上直接进行首次排名,从而提高速度和准确性。优步外卖主要采用两种地理分片技术:纬度分片和六边形分片。

 

纬度分片将世界划分为水平带,每一带代表一个单独的分片。分片范围通过 Spark 作业离线计算得出,首先将地图划分为数千个狭窄的纬度条带,然后将相邻的条带组合起来,以创建大小大致相等的分片。落在分片边界上的文档会在相邻的两个分片中都进行索引,以防止出现结果缺失的情况。

 

纬度分片的一个关键优势在于能够有效地将流量分配到不同的时区。鉴于优步外卖的活动高峰遵循“太阳模式”,即白天需求高,夜间需求低,这种方法有助于防止特定分片上出现过载。然而,在人口密集的城市地区,分片可能会变得不均匀,从而导致索引延迟和查询延迟增加。

 

非地理局部,而是地理线性:使用美国和欧洲重叠的可视化示例

 

为了应对这些挑战,优步外卖还采用了六边形分片,该分片基于H3地理空间索引系统。与将世界划分为带状区域不同,六边形分片将数据组织成不同分辨率的六边形瓦片。选择合适的六边形大小至关重要。优步通常选择 H3 级别 2 或 3 来平衡效率和精度。与纬度分片类似,六边形分片也包含缓冲区,确保靠近分片边界的文档在多个六边形中被索引,以防止搜索出现空白。六边形分片的关键优势在于能够创建更均衡的分片分布,特别是在人口密集的城市地区,而纬度分片在这方面则表现不佳。

 

六边形覆盖热度:一种专为为城市搜索负载均衡而构建的分片策略

 

通过结合这两种技术,优步外卖优化了其搜索基础设施,以提供快速、准确和可伸缩的地理空间查询,为用户带来无缝的体验,无论他们在哪里下单。

 

为了优化优步外卖的搜索性能,通过利用查询模式和优化数据布局来提高召回率并减少延迟,进行了多项改进。其中一项关键改进是针对不同的使用场景(如食品配送和杂货搜索)构建专门的数据布局。通过使数据结构与查询模式对齐,检索效率得到了提高,减少了不必要的计算。另一个重大优化是对预计发货时间(ETD)进行索引,这使得搜索空间能够被划分为不重叠的范围并进行并行处理,从而加快查询执行速度,同时保持准确的排名。

 

将预计到达时间(ETA)作为空间约束:为效率和隔离构建候选检索

 

此外,在索引管道的早期阶段优先处理诸如区分附近和远处商店之类的任务,这减少了查询时的处理时间,并提高了整体性能。这些改进极大地提高了搜索效率,提供了更快、更相关的结果,同时保持了可扩展性。

 

为更快和更可伸缩的查询改进数据布局

优步外卖通过优化数据布局以更好地适应查询模式,从而提高了搜索效率,大幅降低了延迟并增强了可扩展性。外卖索引经过重新构建,首先按城市对餐厅进行分组,然后是单个餐厅,最后是其菜单项。这种方法使得查询能够过滤掉不相关的城市,迅速减少不必要的处理。此外,由于 Lucene 使用差分编码,将相似属性聚集在一起提高了压缩效率,使索引大小减少了 20%。

 

Lucene 中的索引布局:地理和商家感知的文档排序

 

对于杂货搜索,由于杂货库存规模高于餐厅,因此需要采用略有不同的策略。根据线下转化率对店铺进行排名,并在每个店铺内对其商品进行分组。这种布局使系统能够为每个店铺设置检索限制,防止单个店铺返回过多结果,并确保来自多个杂货供应商的结果更加多样化。这对于诸如“鸡肉”这类通用关键词的搜索尤其有用,因为单个店铺就可能出现数千个匹配结果。

 

优化杂货搜索:店铺级分组和基于转化率的排名

 

这些数据布局优化使检索延迟提高了 60%,将查询时间从 145 毫秒减少到 60 毫秒。此外,对文档进行排序使得查找更高效,将每份文档的检索时间从 10 到 60 微秒减少到不到 5 微秒。总体而言,P95 延迟提高了 50%,确保了更快、更平滑的搜索。

 

性能优势:平均延迟和实时延迟减少了近一半

 

优化优步的 ETA 索引

 

优步改进了它的估计到达时间(ETA)索引,以优化搜索效率,减少延迟,并通过将关于餐厅送货区域和估计时间的元数据合并到搜索平台中来提高召回率。最初,该系统缺乏配送区域(六边形)之间的相对距离信息,使得排名器难以有效评估远处的餐厅。为了解决这个问题,优步将送货时间划分为固定的时间范围,并根据餐厅与每个六边形的接近程度对餐厅进行索引。

 

基于 ETA 的六边形索引:为餐厅 R1 和 R2 绘制可达区域

 

这种方法需要在存储空间和处理效率之间进行权衡。如果餐厅落在不同六边形的不同时间范围内,它们必须被多次存储。尽管这增加了存储开销,但它显著提高了查询速度,导致相关结果的检索速度更快。优步测试了其他索引方法,包括 BKD 树和基于 gRPC 的检索,但最终确定预先计算这些关系并将它们存储在索引中能提供最佳的性能。

 

新系统实现了查询并行化,允许单个请求以恒定的延迟触发对不同 ETA 桶的多个搜索。这使得查询延迟减少了 50%,召回率也得到了提高,因为排名器能够在不牺牲性能的情况下获得更多相关的候选餐厅。

 

优化检索:基于 ETA 桶划分的并行范围查询

 

另一个关键的增强是处理不可配送但可发现的店铺。有时餐厅出现在搜索结果中,但由于各种因素,如快递员可用性、位置和一天中的时间,它们不能接受订单。优步没有在查询时动态处理这个问题,而是在数据摄取层预先计算了可配送性。这使得系统能够快速区分可配送和仅可发现的餐厅,提高了用户体验。

 

优步搜索优化的关键方面

 

优步在优化搜索和检索性能的过程中展示了良好结构化索引、高效的分片策略和并行化技术的关键作用。最初,性能问题源于数据存储和检索的低效。这导致查询在处理跨多个区域的大量文档时出现延迟。通过仔细分析查询模式,优步重新组织了其索引布局,优先考虑基于城市和店铺的聚类,确保可以更快地检索相关结果。

 

这次重组将检索时间减少了 50%以上,并提高了压缩比,从而提高了存储效率。。ETA 索引的引入通过“惩罚”遥远的店铺,同时优先考虑那些在最佳交付范围内的店铺,进一步完善了排名过程。通过战略性的数据摄取和索引,优步优化了召回率和延迟之间的平衡,确保用户能够更快地获得更相关的店铺结果。

 

搜索索引性能提升:通过执行范围查询降低了 50%的延迟

 

除了重组索引,优步还专注于将复杂的回退操作从查询层转移到摄取层。这种转变减少了查询执行期间的不必要处理,简化了搜索体验。并行化范围查询的使用使优步能够在不影响延迟的情况下扩大选择空间,增强了搜索结果的多样性,同时保持了快速的响应时间。通过移除处理测试店铺和生产数据的低效性,系统进一步优化,这曾使查询时间膨胀。通过使用不重叠的子查询,优步最大化了并行化,并启用了各种匹配类型的同时执行,包括强匹配、模糊匹配和部分关键字匹配,以提高效率。

 

结论

这些优化需要广泛的跨团队合作,需要搜索、首推、广告团队在影响整个搜索生态系统的变更上达成一致。协调这些工作是复杂的,因为每个团队都需要确保他们各自的服务能够无缝集成。这个过程跨越了几个月的基准测试、测试和微调,但结果是变革性的。延迟显著降低了,召回率得到了提高,系统可伸缩性也得到了相当大的改进,为优步外卖提供响应更快和更直观的搜索体验奠定了基础。

 

随着优步在全球范围内不断扩展和完善其平台,这些进步凸显了数据驱动决策、高效系统设计和持续迭代的重要性。通过持续评估和优化系统,优步确保它在大规模分布式系统的快速发展环境中保持敏捷和竞争力,为用户提供越来越无缝的体验,并支持其不断扩大的服务范围。

 

原文链接:

https://www.infoq.com/articles/optimizing-search-systems/

2025-07-17 11:009035

评论

发布
暂无评论

易观千帆 | 2023年4月证券APP月活跃用户规模盘点

易观分析

证券

理解 JVM 工作机制(八) JVM 性能调优

Geek漫游指南

Java jdk JVM

CMake入门教程:从零开始构建C/C++项目

小万哥

程序员 后端 开发 C/C++ cmake

3D设计必备!5个免高质量的 HDRI 环境贴图网站

Finovy Cloud

blender

理解 JVM 工作机制(六) 垃圾收集器

Geek漫游指南

Java jdk JVM

中国互联网广告市场年度分析2023

易观分析

互联网 广告

2D CAD设计软件CADintosh X 激活版

真大的脸盆

Mac Mac 软件 CAD绘图 CAD设计 cad

聊聊 Milvus GC:从一次数据丢失事件展开

Zilliz

非结构化数据 GC Milvus 向量数据库

Nautilus Chain开启全球行,普及Layer3概念加速其采用

BlockChain先知

理解JVM工作机制(五)垃圾回收算法

Geek漫游指南

Java jdk JVM

理解 JVM 工作机制(七) 内存分配和回收策略

Geek漫游指南

Java jdk JVM

理解 JVM 工作机制(十) 类加载机制和加载的过程

Geek漫游指南

Java jdk JVM

软件测试|Python操作Excel制作报表,不要太方便

霍格沃兹测试开发学社

软件测试|简单易学的性能监控体系prometheus+grafana搭建教程

霍格沃兹测试开发学社

理解 JVM 工作机制(十一) 类加载器

Geek漫游指南

Java jdk JVM

如何使用golang实现桥接模式

Jack

AIGC背后的技术分析 | 图像风格迁移

TiAmo

AIGC 图像风格迁移

理解 JVM 工作机制(四) 回收策略

Geek漫游指南

Java jdk JVM

理解 JVM 工作机制(九) 类文件结构

Geek漫游指南

Java jdk JVM

2023-05-24:为什么要使用Redis做缓存?

福大大架构师每日一题

redis 福大大

c#中用System.Diagnostics.Process.Start(Path.GetFullPath(“vlc.exe.lnk“), url);用vlc的快捷方式打开http的url不起作用?

福大大架构师每日一题

C# 福大大 vlc

基于 Amazon API Gatewy 的跨账号跨网络的私有 API 集成

亚马逊云科技 (Amazon Web Services)

Amazon

Solaris Exchange:一个安全可靠的合成资产交易平台

股市老人

ConcurrentHashMap是如何实现的?

javacn.site

人体识别图像技术在智能家居中的应用

数据堂

软件测试|Python实用炫酷技能——推导式

霍格沃兹测试开发学社

C语言编程—循环语句

芯动大师

Nautilus Chain开启全球行,普及Layer3概念加速其采用

西柚子

开发者们:618电商团战即将开启,“抢流量”想上分,必备这三个大招 | MobTech观察

MobTech袤博科技

Nautilus Chain开启全球行,普及Layer3概念加速其采用

股市老人

优化搜索系统:平衡速度、相关性和可伸缩性_大数据_InfoQ精选文章