
想象一下,在旅游网站上搜索航班,结果加载需要等待 10 秒。感觉像一个世纪那么长,对吧?现代旅游搜索平台即使在高负载下也必须几乎即时返回结果。然而,不久前,我们旅游搜索引擎的 API 的 p95 延迟徘徊在 10 秒左右。这意味着 5% 的用户搜索(通常是在高峰流量期间)需要 10 秒或更长时间。结果——用户失望、跳出率高,更糟的是——销售额损失。因此,在这种情况下,降低延迟是不可妥协的。
本文是一个真实案例研究,介绍了我们如何改进云基础架构以消除延迟问题。通过利用 Google Cloud Run 进行可扩展计算并使用 Redis 进行智能缓存,我们将搜索 API 的 p95 延迟从约 10 秒缩短至约 2 秒。在这里,我们将介绍降低延迟的整个过程。其中包括性能瓶颈、优化以及它们带来的显著改进。
极高的延迟是一个严重的问题。深入研究后,我们发现多个因素拖累了我们的响应时间。所有这些因素都有一个共同点——它们使我们的搜索 API 在每个请求上都承担了大量的繁重工作。在实现整体延迟降低之前,我们必须解决以下问题:

所有这些因素共同导致了 p95 延迟过慢。从根本上讲,我们的架构并未针对速度进行优化。查询执行了冗余工作,而我们的基础架构并未针对延迟敏感的工作负载进行调整。好消息是?每个瓶颈都是降低延迟的机会。
我们主要从两个方面来降低延迟:缓存以避免重复工作;Cloud Run 优化以更大限度地减少冷启动和处理开销。后端的改进过程如下:
我们部署了 Redis 缓存,以缩短热路径上高开销操作的执行时间。其理念非常简单:存储频繁或近期查询的结果,并直接将其提供给后续请求。例如,当用户搜索特定日期从纽约飞往伦敦的航班时,我们的 API 会获取并编译一次结果。然后,它会将该“票价响应”缓存在 Redis 中一小段时间。
如果另一个用户(或同一个用户)随后进行了相同的搜索,后端可以在几毫秒内返回缓存的票价数据,从而避免重复调用外部 API 和数据库查询。通过避免在缓存命中时进行高开销的上游调用,我们显著降低了热查询的延迟。
我们也对其他数据应用了缓存,例如静态或缓慢变化的参考数据。例如机场代码、城市元数据、货币汇率,现在都使用缓存。服务现在不再每次请求都访问数据库查找机场信息,而是从 Redis 中检索(在启动或首次使用时加载)。这减少了许多细微的查找操作,这些查找操作会不时增加几毫秒的时间(在负载下这些时间会累积起来)。
根据经验,我们决定“缓存热门数据”。热门航线、最近获取的价格以及机场信息等静态参考数据都保存在内存中,方便随时访问。为了保持缓存数据的新鲜度(在价格变化时至关重要),我们设置了合理的 Ttl(生存时间)到期时间和失效规则。例如,票价搜索结果最多缓存几分钟。
之后,它们就会过期,以便新的搜索能够获得最新的价格。对于高度波动的数据,我们甚至可以在检测到更改时主动使缓存条目失效。正如 Redis 文档所述,机票价格通常“每隔几个小时”才会更新一次。因此,较短的 TTL 与基于事件的失效机制相结合,可以在新鲜度和速度之间取得平衡。
结果如何?缓存命中后,每个查询的响应时间从几秒缩短到几百毫秒甚至更短。这全都归功于 Redis,它能够通过内存以极快的速度提供数据。事实上,行业报告显示,使用内存中的“票价缓存”可以将需要数秒的航班查询转化为仅需几十毫秒的响应。虽然我们的结果并非完全即时,但这个缓存层带来了巨大的提升。显著降低了延迟,尤其是在重复搜索和热门查询方面。
缓存有助于处理重复性工作,但我们还需要优化首次查询和扩展的性能。因此,我们对 Cloud Run 服务进行了微调,以实现低延迟。
我们为 Cloud Run 服务启用了最小实例数 = 1。这确保即使在空闲期间也至少有一个容器处于启动状态并准备好接收请求。之一个用户请求不再会受到冷启动惩罚。Google 工程师指出,保持最小实例数可以显著提高对延迟敏感的应用的性能,因为它消除了从零到一的启动延迟。
在我们的案例中,将最小实例数设置为 1(高峰时段甚至可以设置为 2 或 3),意味着用户无需等待容器启动。仅凭这一项优化,p95 延迟就显著下降。
我们重新审视了并发设置。在确保代码能够安全地处理并行请求后,我们将 Cloud Run 的并发数从 1 提升到更高的数字。我们尝试了 5、10 等值,最终将我们的工作负载设置为 5。这意味着每个容器在需要启动新实例之前最多可以同时处理 5 个搜索请求。
结果——流量高峰期间生成的新实例更少。这反过来又意味着更少的冷启动和更低的开销。本质上,我们让每个容器并行执行更多工作,直到 CPU 使用率仍然保持健康。我们密切监控 CPU 和内存——我们的目标是高效利用每个实例,而不会使其过载。
这种调整有助于平滑突发流量下的延迟:如果同时有 10 个请求,而不是 10 个冷启动(并发度 = 1),我们会使用 2 个热实例,每个实例处理 5 个请求,从而保持快速响应。
我们还进行了一些应用级别的调整,以加快 Cloud Run 的启动和运行速度。此外,我们还启用了 Cloud Run 的启动 CPU 提升功能,该功能可在启动期间为新实例提供 CPU 的爆发式增长。我们还使用了精简的基础容器镜像,并在启动时仅加载必要的模块。
某些初始化步骤(例如加载大型配置文件或预热某些缓存)也被移至容器启动阶段,而不是在请求时执行。得益于最小实例数,这种启动过程的运行频率很低。实际上,当请求到达时,实例已经完成引导(数据库连接打开、配置加载等),因此它可以立即开始处理查询。
我们实际上只支付了一次启动成本,然后在多个请求中重复使用它,而不是在每个请求上都支付少量成本。
实施这些优化后,效果立竿见影。我们监控了 API 的性能,对比了优化前后的对比。p95 延迟从大约 10 秒骤降至 2 秒左右。对于我们的用户来说,这带来了令人惊叹的 5 倍加载速度提升。平均延迟也得到了改善(对于缓存命中查询,延迟通常小于 500 毫秒)。
更重要的是,响应变得一致且可靠。用户不再需要忍受痛苦且令人恐惧的 10 秒等待。系统能够从容应对流量高峰:Cloud Run 可以根据需要扩展至更多实例。凭借热容器和更高的并发性,系统能够轻松应对,避免冷启动带来的拥堵。
同时,Redis 缓存吸收了重复查询,并降低了下游 API 和数据库的负载。这还通过防止这些系统成为瓶颈,间接降低了延迟。
最终结果是,搜索 API 更加敏捷、更具可扩展性,满足了客户对快速响应和流畅体验的期望。
在我们为降低延迟而进行的一系列优化中,以下是一些关键要点和值得您考虑的要点。
虽然这些降低延迟的策略似乎难以一次性全部实现,但在实践中,系统地逐一检查每个策略都非常顺利。整个测试过程中,我们更大的亮点在于,我们将旅行搜索 API 从迟缓的体验提升到了即时的体验。在用户期望“昨天”就能得到答案的时代,将 P95 延迟从 10 秒缩短到 2 秒,对于提供流畅的旅行搜索体验至关重要。
宝塔面板现在已经成为国内许多站长必备的服务器管理必备工具。相比直接使用SSH+FTP来管理服务器,宝塔面板可以提供可视化管理,包括文件管理、数据库管理、数据备份、SSL配置等等。 如果你希望更简单高效地管理您的网站及服务器,宝塔面板是不错的选择。下面是一些宝塔面板安装及常见问题:...
使用宝塔面板,您可以快速地创建一个FTP管理账户,对网站文件进行管理。但有必要提醒大家的是,使用FTP远不如使用SFTP安全,你可以查看文章“”进一步了解两者之间的差异。 此外,宝塔面板的文件管理模块其实已经能够满足站长的大部分文件管理需求。当然,如果你非得要使用FTP管理服务器文件,可以参照以下...
由于市场上有各种可用的数据库,用户经常会就MongoDB与MySQL进行辩论,以找出更好的选择。 使用MySQL等关系数据库的组织在根据不断变化的需求管理和存储数据时可能会面临一定的困难。同时,新公司想知道选择什么数据库,这样他们就不会在开发过程中遇到问题。 同时,构建金融应用程序的开发人员...
Laravel多年来一直是PHP应用程序开发的摇滚明星,这是有充分理由的。庞大的生态系统、活跃的社区、强大的就业市场、成功的初创公司——它拥有一切让采用新技术变得值得的东西。 如果你想学习Laravel,你不需要更进一步。通过浏览本指南,您可以找到最适合您的Laravel教程,与您的知识水平和...
PHP开发:有些人认为它是,另一些人认为它是一种过时的技术,不值得花时间学习。但是这些数字说明了什么,尤其是当您查看PHP开发人员的薪水时? 虽然肯定有许多更新、更炫、更令人兴奋的语言,但PHP仍然是后端Web开发的可靠主食,也是许多雇主仍在寻找的技能。 有兴趣自己成为PHP开发人员吗?我们...
无服务器计算是一种基于云的执行模型,可以将应用程序作为服务托管,而无需维护服务器。 服务提供商维护服务器上的资源分配,并根据实际使用情况向用户收费。焦点转移到一个人正在创建的核心应用程序上,基础设施完全由服务提供商处理。无服务器计算也称为功能即服务 (FaaS)。 换句话说,Serverle...