2. n+1 问题
10m

概述

我们的 API 已针对提供一些基本列表数据进行了配置。我们可以针对特色列表运行 ,或请求某个特定列表。

此外,对于每个列表,我们还可以 每个 便利设施它提供的数据。但现在,我们面临着与如何实现此项功能有关的巨大性能问题。

在本课程中,我们将

  • 了解 n+1 问题
  • 讨论如何解决此问题

列表和便利设施

为观察我们的性能瓶颈的实际表现,我们将针对我们 API 运行测试 。它将查询特色列表的清单,以及关于每个列表的便利设施的一些基本详情。

A mock-up for Airlock, showing a row of featured listings and their amenities

通过在项目根目录中运行以下命令,确保应用已在运行。

./gradlew bootRun

现在,让我们导航至Apollo Sandbox Explorer,并粘贴屏幕顶部输入框中本地运行的服务器地址。默认情况下,我们的服务器应 https://127.0.0.1:8080/graphql上运行。

https://127.0.0.1:8080/graphql
https://studio.apollographql.com/sandbox/explorer

A screenshot of the Apollo Sandbox Explorer, highlighting the connection input with the locally running server's address

让我们通过从 类型的文档面板中选择 featuredListings 来开始我们的

对于我们 的每个特色列表,我们将请求基础信息:只是 idtitle,以及其 amenities 列表。

对于该列表中的每个 Amenity,我们将返回 idnamecategory

以下是我们的 的示例。
query GetFeaturedListingsAmenities {
featuredListings {
id
title
amenities {
id
name
category
}
}
}

针对特色列表及其便利设施的查询

接下来,我们将仔细观察服务器运行所在的终端来探究其中的缘由。再次运行,然后……你注意到它了吗?终端中充斥着以下内容的日志:

每次调用 REST API 时的输出
Calling for featured listings
Calling for amenities for listing listing-1
Calling for amenities for listing listing-2
Calling for amenities for listing listing-3

我们看到这里为每个清单 ID 打印了一行,而每一行恰恰表示网络上对单个发出的请求。请求数量远远超出我们对简洁、精准的 的预期!下面我们深入探讨一下此处的情况。

每个清单一个新请求

问题在于我们为精选清单列表制作了一个请求,以及为每个清单的便利设施列表制作了一个附加请求。

以下是如何解决我们的来获取特色列表及其便利设施。

为了获取特色列表,我们的数据取值程序首先调用ListingService方法,该方法向GET /featured-listings端点发出请求。这会返回一个 JSON 对象,其中包含我们的基本列表详细信息。

A diagram showing the data that is returned when we query for featured listings

但是,此响应实际上不包含有关列表便利设施的任何信息。这意味着会对GET /listings/{listing_id}/amenities发出另一个请求,其中将它的 ID 传递为{listing_id}参数。

A diagram showing the followup request needed for each listing's amenities

此额外请求为我们获取了所需的便利设施数据,但它有一个隐藏的成本:每当Listing.amenities数据取值程序执行时,我们就会向 REST API 发出新请求来获取便利设施数据。

n+1 问题

这是实际操作中的n+1 问题。我们从一个初始请求开始(这是1n+1方程式中的n+1方程式中的1),此第一个请求确定需要多少后续请求(即n+1方程式中的n)。在执行我们的第一个请求之前,不知道所需的后续请求数n

我们在实际操作中看到了这一点:我们的第一个请求给出了我们的特色列表(有三个),但我们随后需要每个发出一条后续请求来获取列表的便利设施数据。

即使只增加一两个请求,这看起来也不会影响很多,但随着查询的扩展,它会导致一些令人担忧的情况。设想一个列表包含二十五个清单(“25 个零下夏季目的地!”);为这样的列表填充数据意味着我们将发送 26 个请求!一个请求来获取清单数据,以及 25 其他请求来获取每个清单的便利设施信息!

实践

以下哪些情况说明了 n+1 问题?

要点

  • 当我们发出一个初始请求,随后发出一些未知数量的跟进请求时,就会发生 n+1 问题。

下一步

让我们深入了解数据加载器以及它们如何帮助我们解决这个棘手的问题。

上一步

分享你的问题和此课程的评论

此课程当前

测试版
.你的反馈有助于我们改进!如果你遇到困难或困惑,请告诉我们,我们会帮助你。所有评论都是公开的,并且必须遵守 Apollo 行为准则。请注意,已解决或已解决的评论可能会被删除。

你需要一个 GitHub 帐户才能在下面发帖。没有帐户? 转而在我们的 Odyssey 论坛中发帖。