加入我们,于10月8日至10日在纽约市,了解有关 GraphQL 联邦和 API 平台工程的最新技巧、趋势和新闻。加入我们,共襄盛举 GraphQL 峰会2024 (NYC)
文档
免费开始

联邦是如何处理N+1查询问题的

学习如何在联邦图中处理返回列表的操作的N+1问题

联邦子图

开发者很快就会遇到著名的“N+1 问题”,这个问题在返回列表的操作中尤为常见:

query TopReviews {
topReviews(first: 10) {
id
rating
product {
name
imageUrl
}
}
}

在单体服务器中,执行引擎采取以下步骤:

  1. 解析Query.topReviews ,它返回一个Review列表。
  2. 对于列表中的每个Review,解析Review.product字段

如果Query.topReviews字段返回10条评论,则执行器将解析Review.product字段 10次。如果Reviews.product字段针对单个Product执行数据库或REST查询,则会看到对的10个唯一调用。这从以下原因看是不太理想的:

  • 一次性查询10个产品更有效率(例如SELECT * FROM products WHERE id IN (<product ids>))。
  • 如果有任何评论引用了同一产品,那么我们正在浪费资源去获取已经拥有的数据。

针对单体GraphQLAPI的解决方案是dataloader模式。所有 GraphQL服务器实现都支持这种模式。Apollo服务器文档解释了如何在Node.js服务器中使用JavaScript实现

联邦图中的N+1问题

考虑相同的TopReviews ,但我们已经在单独的子图中实现了ReviewProduct类型

幸运的是,默认处理Product类型等实体的N+1查询!此操作的查询计划如下:

  1. 首先,我们使用根字段Query.topReviewsReview子图Fetch列表中的Review。我们还请求每个相关产品的id
  2. 接下来,我们提取 Product 引用并将其批量检索到 Products 子图中的 Query._entities 根字段。
  3. 返回 Product 实体后,我们在 Flatten 步骤中将它们合并到 Review 列表中。

Fetch (reviews)
Fetch (products)
Flatten (topReviews,[],products)

编写高效的实体解析器

在大多数 subgraph 实现(包括 @apollo/subgraph)中,我们不直接编写 Query._entities 。相反,我们使用 引用解析器 API 来解析单个实体引用:

const resolvers = {
Product: {
__resolveReference(productRepresentation) {
return fetchProductByID(productRepresentation.id);
},
},
};

此 API 的动机与 subgraph 规范的微妙、关键方面相关:解析实体的顺序必须与给定实体引用的顺序匹配。如果我们以错误的顺序返回实体,则这些 将与错误的实体合并,我们将得到错误的结果。为了避免此类问题,大多数 subgraph 库会为您处理实体顺序。

但这会再次引入 N+1 查询问题:在上面的示例中,我们将为每个实体引用调用一次 fetchProductByID

幸运的是,在单体图中也存在相同的解决方案:数据加载器。在几乎所有情况下,引用 都应该使用数据加载器。

const resolvers = {
Product: {
__resolveReference(product, context) {
return context.dataloaders.products(product.id);
},
},
};

现在,当调用带有产品实体批的产品子图时,我们将对产品数据源发出单个批处理请求。

接下来
首页
评价文章评价在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.,业务名称Apollo GraphQL。

隐私策略

公司