3. 数据加载器揭秘
10m

概述

在本课中,我们将

  • 讨论什么是数据加载器
  • 介绍dataloader
  • 回顾数据加载器的要求
  • 详细了解数据加载器在底层的运作方式

数据加载器

为了解决我们应用程序中的 n+1 问题,我们将使用 数据加载器

数据加载器的主要工作是将多个相似的请求替换为一个批处理请求。在我们的示例中,我们看到了三个几乎完全相同的请求,它们使用特定的展示 ID 来返回 tiện nghi 数据。使用数据加载器,这会变成一个 单个 请求,一次获取所有三个展示的数据。

A diagram showing listing Ids being batched by a data loader

我们将会向我们的 类中引入数据加载器的强大功能,并使用 GraphQL dataloader

dataloader

dataloader 包是一个实用程序,它提供批处理和缓存功能。这让我们 各种键批处理在一起,使用一个请求,一次性提供所有数据!

让我们用以下 来对此进行说明。

有关精选展示及其 tiện nghi 的查询
query GetFeaturedListingsAmenities {
featuredListings {
id
title
amenities {
id
name
category
}
}
}

对于查询解析出的每个精选列表对象,将调用 当前,此解析器使用列表 ID 来独立解析 Listing.amenities 。每个请求。(导致对同一端点的三个单独的网络请求!)

我们想要的行为截然不同:我们希望数据加载器收集 所有参与 (也称为 密钥)的列表 ID,并执行一个请求。

在我们的示例中,这意味着在使用每个列表的 ID 调用 Listing.amenities 时,它不会直接调用命中我们 REST API 的 ListingAPI的方法;而会将每个密钥传递给一个数据加载器方法,它可以将整个 所需的密钥批处理在一起。

A diagram showing listing Ids batched by a data loader

在将所有列表 ID 收集到一个列表中后,数据加载器可以承担从 实际请求数据的责任。它可以一次向 REST API 端点分派一个请求,用于所有 ID同时——与让 每个发起网络请求相比,性能大幅提升!

最棒的是,我们的数据加载器方法会自动对我们传递给它们的标识符进行重复数据删除。这意味着,如果我们的 包含多个具有相同 ID 的列表,我们只会请求一次列表的便利设施。

A diagram showing the data loader making a single REST request with all the collected IDs

数据加载器需要什么

数据加载器正是我们解决应用中性能问题所需的东西,但它们包含需我们考虑的一些要求。我们逐一了解其中的每一个要求。

一次针对多个对象的数据

假设我们的数据加载器收集了我们的中涉及的所有不同密钥,并且准备发出数据请求。它接下来需要什么呢?

嗯,如果我们思考一下之前用于返回便利设施数据的 REST API 端点,我们会很快发现问题:现在,Listing.amenities 依赖于ListingAPI方法getAmenities将每个列表 ID 单独发送到GET /listings/{listing_id}/amenities端点,它仅为一个单个列表返回数据。

这让我们的数据加载器按预期工作有了第一个重要的要求:我们需要一个能够同时解析多个对象的请求。在实践中,这意味着我们的数据加载器应该能够发送一串密钥(例如["listing-1", "listing-2", "listing-3"]),并获取所有这些密钥的数据。

好消息是,我们的 REST API 中确实有不同的端点,我们可以使用它来请求多个列表的便利设施数据:GET /amenities/listings它接受多个列表 ID,以一个称为ids参数字符串的形式连接,一次性返回它们的所有数据。

接收多个键并返回多个值的 REST 端点
GET /amenities/listings?ids=listing-1,listing-2,listing-3

每个键一个值

当数据加载器在请求中发送一串密钥时,它对提供数据的有非常明确的期望:返回的对象数量决不能大于请求中发送的密钥数量。返回的值也应与请求的相应密钥具有相同的顺序。

让我们分解一下这个期望以及如何满足它。

在解决的过程中,我们的可能会调用数据加载器三次,向其传递三个键。数据加载器将它们分组到一个列表中(["listing-1", "listing-2", "listing-3"]),然后使用它们来调用GET /amenities/listings。它期望得到什么返回结果?嗯,它放入了一个列表,包含三个键;它期望返回一个包含不超过三个对象列表!

A diagram showing a request with three listing IDs; three lists of amenities are returned

对于每个请求的键,我们期望我们的返回一个便利设施列表。这是因为每个清单都可以具有多个与之关联的便利设施。因此,如果我们请求三个列表 ID 的便利设施数据,则我们的响应应该包含三个便利设施列表。

A diagram showing three listing IDs; and three lists of amenities that map to them

注意:数据加载器还期望每个返回的对象与原始请求中对应键的位置保持一致。例如,如果将“listing-1”作为第一个键发送,则其便利设施列表应该是响应中的第一个对象!

从那里,数据加载器处理将每个便利设施列表映射回请求它的键的逻辑。

数据加载器作用域

需要记住的最后一个重要要点是。必须在一次请求中限制数据加载器和它们在任何时间处理的键集合。因此,我们在下一课中的实现将确保每个请求都会创建一个新的 DataLoader 实例。

这意味着,如果我们运行一个 来列出数据,然后依次运行第二个查询,那么这两个查询中的键将不会被批量处理到一起。相反,每个查询将被单独解析。

A diagram demonstrating how a data loader handles two queries separately

明确了这些概念性要点之后,让我们将注意力重新转向代码。我们将更新我们的 Listing.amenities 以在我们的 ListingAPI 类中调用一个 新的方法,并从数据加载器中获益!

练习

以下哪些数据加载器语句是正确的?

关键要点

  • 数据加载器让我们可以将一个 ID(例如 ID)的列表批量处理在一个请求中,而不是为每个 ID 发送一个单独的请求。
  • 在我们 (另一个 API 或数据库)能够正常工作之前,数据加载器需要实现一个方法,该方法接受多个键(例如 ID),并返回多个对象。
  • 数据加载器从 接收到的对象数量不应超过数据加载器收集的键的数量。(例如,如果数据加载器请求 三个 listing 的数据,它最多只应收到 三个 listing 对象!)

接下来

我们已经了解了数据加载器及其解决我们应用程序中遇到问题的解决方案。我们还有一个新的 方法,该方法接受 多个清单 ID,并解析多个便利设施对象。接下来,我们将实现数据加载器逻辑,以便在单个请求中收集多个清单 ID。

上一个

分享你对本课程的问题和评论

本课程目前处于

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

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