概述
我们的GraphQLAPI 已准备好提供一些基本列表数据。我们可以运行 查询查找特色列表,也可以专门查找某个列表。
此外,对于每个列表,我们还可以 查询获取有关每个 Amenity
它必须提供的详细信息。但现在,我们在实现过程中遇到了一个重大的性能问题。
在此课程中,我们将
- 了解 n+1 问题
- 讨论如何解决此问题
列表和便利设施
为了了解我们性能瓶颈的实际情况,我们针对 GraphQLAPI 运行一个测试 查询。它会查询一个特色列表,以及每个列表的便利设施的一些基本详细信息。
确保应用的运行方式是在项目的根目录中运行以下命令。
npm run dev
现在,让我们导航至Apollo Sandbox Explorer. 默认情况下,我们的服务器应该在 https://127.0.0.1:4000
上运行。
https://127.0.0.1:4000
让我们通过从 Query
类型中的 文档 面板中选择 featuredListings
字段 来开始我们的 查询。对于我们 查询的每个特色房源,我们将请求基本信息:仅 id
和 title
,以及其 amenities
列表。
对于房源享有的每个 Amenity
,我们将返回 id
、name
和 category
。
以下是我们的 查询的样子。
query GetFeaturedListingsAmenities {featuredListings {idtitleamenities {idnamecategory}}}
让我们尝试一下这个 查询,然后... 我们得到了数据!太棒了。那么问题究竟出在哪里?
为了找出原因,我们将仔细查看服务器正在运行的终端。再次运行 查询,然后... 你注意到了吗?终端被记录语句填满:
Calling for featured listingsCalling for amenities for listing listing-1Calling for amenities for listing listing-2Calling for amenities for listing listing-3
首先,我们看到一行日志记录显示我们对特色列表发起了调用。然后,我们看到为每个列表 ID 打印出一行;并且每行代表一个单个请求,该请求通过网络发送到我们的数据源。我们精简而准确的GraphQL 查询产生的请求数量可能多于我们的预期!我们深入了解一下这里发生的情况。
对于每个列表,都是一个新的请求
这里的问题在于,我们针对特色列表列表发出一个请求,并针对每个列表的便利设施列表发出一个其他请求。
以下是关于如何解决精选房源及其便利设施的查询的细分。
要获取精选房源列表,我们的解析程序首先调用ListingAPI
方法,该方法向GET /featured-listings
端点发出请求。这会返回包含我们的基本房源详细信息的 JSON 对象。
但此响应实际上并不包含有关房源便利设施的任何信息。这意味着我们会对每个房源发出另一个请求GET /listings/{listing_id}/amenities
,并传入其 ID 作为{listing_id}
参数。
这个额外的请求会获取我们需要的便利设施数据,但它有一个隐藏的成本:每次执行Listing.amenities
解析程序时,我们会针对便利设施数据向 REST API 发出新的请求。
n+1 问题
这就是n+1 问题正在执行中。我们从一个初始请求(1
在n+1
等式中)开始,而这个初始请求确定了需要多少个后续请求(n
在n+1
等式中)。必需的后续请求数量n
在执行我们的第一个请求之前是未知的。
我们看到这个问题:我们的第一个请求会给我们精选列表(有三个),但我们随后需要每个列表的后续请求来获得列表的便利设施数据。
只有 1 到 2 个附加请求看起来不是太糟糕,但随着查询的扩展,这会导致一些令人担忧的情况。假设一个列表包含二十五个列表(“25 大零下夏季目的地”!),填充这样的列表数据意味着我们需要发送总计 26 个请求!请求获取列表数据,以及 25 个附加请求来获取每个列表的便利设施信息!
实践
关键要点
- 当我们发出一个初始请求,随后是未知数量的后续请求时,就会发生 n+1 问题。
接下来是
让我们深入研究数据加载器以及它们如何帮助我们解决这个讨厌的问题。
分享您对这节课的问题和意见
此课程当前采用
您需要一个 GitHub 帐户才能发布以下内容。没有一个帐户? 改为在我们的 Odyssey 论坛中发帖。