Uber Eats 最近把搜索链路的端到端延迟砍掉了 50%,但最值得玩味的不是某个单项优化有多惊艳,而是他们整个思路的转变:从"把每件事做得更快"变成"少做点事、别傻等"。这个思路对任何做后端性能优化的工程师都有直接参考价值。
先看他们踩的第一个坑:衡量指标选错了。以前 Uber 盯着后端 API 响应时间,但用户真正感知到的其实是"首屏结果带图渲染完成"的时间,他们管这个叫 Above-the-Fold completion。这俩指标差距很大——API 返回快不代表页面渲染快,尤其当结果里要加载一堆图片时。于是他们改用这个指标,配合服务端缓存的分页(先把第一页结果快速返回,后续页再慢慢补)和异步渲染(结果条目并发处理,而不是串行等),首屏延迟直接降了 200 多毫秒。这给我们的启发很直接:先搞清楚用户到底在等什么,再决定优化哪里,否则可能一直在优化一个用户感知不到的环节。
接下来是检索阶段的瘦身。Uber 发现一个很浪费的现象:排序前会先"水合"(hydration,就是把候选结果的完整数据从存储里捞出来准备好)几万个候选,但排序后大部分都被丢弃了。这就像面试来了 100 个人,你给每个人都准备了全套档案,最后只录用了 5 个。他们砍掉了一些低价值的检索策略,省了约 120 毫秒;又改用产品级 embedding(把商品映射成向量,用向量相似度做检索,而不是逐条查数据库),数据查询量直接降了 100 多倍,再省 50 毫秒。
排序阶段也有大文章。他们把"排序用的数据"和"展示用的数据"分开水合——排序只需要价格、评分这些轻量字段,展示才需要图片 URL、描述这些重数据。以前混在一起,排序阶段就得把展示数据也一起捞出来,白白等很久。拆开之后省了 100 多毫秒,再通过去掉不必要的依赖和请求对冲(request hedging,同时发多个请求,谁先回来用谁)又各省了 35 和 40 毫秒。广告路径也重写了,改用列式存储的竞价数据、内存访问、减少序列化,省了约 130 毫秒。基础设施层面还做了并行编码、更小的 embedding、连接管理优化,以及调整 Go 的数据结构来减少垃圾回收(GC,自动回收不再使用的内存,但会暂停程序运行)的开销。
整个优化过程里,最让我意外的是他们用了一个 agentic coding workflow(智能体编程工作流,就是让 AI 自动去识别、基准测试、验证优化点),这算是把 AI 用在了性能优化这种需要大量排查的活上。
有工程师把 Uber 的方法论总结成三个原则:少干活、早开工、去掉不必要的依赖。这听起来朴素,但做起来极难——每个环节都觉得自己那点活是必须的,只有站在全链路视角才能看清哪些是浪费。Uber 还在探索端到端微批处理(microbatching,把请求分成小批次处理,让不同阶段能重叠执行而不是等上一阶段完全结束),早期测试显示 p99 延迟(99% 的请求都能达到的延迟上限,代表最差情况下的体验)又降了 50% 以上。
这套思路能直接用在你自己的场景里:先重新审视你的延迟指标是不是用户真正感知的那个;然后画一遍全链路,找出"捞了大量数据但只用了一小部分"的地方;最后想想哪些阶段能并行或重叠,而不是傻等。最大的坑可能是惯性——团队往往习惯了"优化=让现有步骤更快",而忘了问"这一步真的需要吗"。

内容与图片版权归原作者所有 · 原文: https://www.infoq.com/news/2026/10/uber-eats-search-latency/?utm_campaign=infoq_content&utm_source=infoq&utm_medium=feed&utm_term=global