使用 DataLoader 解决 GraphQL 中 N+1 查询问题

阅读时长 8 分钟读完

什么是 N+1 查询问题

在 GraphQL 中,一个查询可能会涉及到多个数据源,如果每次查询都去请求对应的数据源,那么就会出现 N+1 查询问题。N 表示需要查询的数据量,当 N 增加时,查询所耗费的时间和资源也随之增加。

下面看一个例子:

上面的查询中,我们需要获取所有用户的名称以及他们所写的文章的标题。如果直接去请求数据源,那么查询过程中将会发送 N+1 次请求:第一次请求获取所有用户的名称,接下来的 N 次请求,则是每个用户的文章标题。

如何解决 N+1 查询问题

一种解决 N+1 查询问题的有效方式是使用 DataLoader。DataLoader 是一个较为通用的库,专门用来解决 N+1 查询问题。

在使用 DataLoader 之前,我们需要了解它的几个组成部分:

  • Batch function: 用于定义如何将 keys 数组(缓存中不存在的 key)转换为对应的 values 数组,并从数据源中获取数据。
  • DataLoader options: 这些选项用于配置 DataLoader 的行为,如缓存大小、缓存策略等。
  • Cached DataLoader: 使用 LRU 缓存算法实现的 DataLoader。缓存可以用来避免重复请求相同的数据。

下面,我们来看一下如何在 Node.js 应用程序中使用 DataLoader。

安装 DataLoader

使用 DataLoader

在 Node.js 应用程序中使用 DataLoader 的一般步骤如下:

  1. 加载依赖
  1. 创建 Batch function

Batch function 是一个用于将 keys 数组转换为对应的值数组以及从数据源中获取数据的函数。

下面是一个最简单的 Batch function 的实现:

batchFunction 接收一个 keys 数组作为参数,返回一个 Promise,Promise 返回的数据是对应 keys 的值数组。在上面的例子中,我们假设 data 是一个全局对象,用来存储从数据源中获取的数据。根据处理过程中实际情况,batch function 的实现可能会有所不同。

  1. 创建 DataLoader

使用 Batch function 创建 DataLoader:

这个 DataLoader 的 Batch function 是 batchFunction,当使用这个 DataLoader 时,它会将 keys 数组作为 Batch function 的参数,然后返回对应的值数组。例如:

如果一次性要查询多个 key,可以使用 loadMany 方法:

  1. 缓存 DataLoader

在 DataLoader 中使用缓存可以避免重复请求相同的数据。

在创建 DataLoader 时,可选的选项 cache 指示 DataLoader 是否要缓存数据。如果设置为 true,则 DataLoader 会使用 LRU 缓存算法来缓存数据。查询相同的 key 时,会直接从缓存中获取数据,而不是从数据源中获取。

  1. 并发 DataLoader

为了获得更好的性能,我们可以通过在 DataLoader 实例之间共享相同的 Batch function 从而实现并发 DataLoader。

在这种情况下,我们可以在 DataLoader 实例之间共享 Batch function 和 LRU 缓存。

使用 DataLoader 解决 N+1 查询问题

下面,我们通过一个样例来演示如何使用 DataLoader 解决 N+1 查询问题。在样例中,我们创建一个 User 和 Posts 数据源,其中 User 通过 id 查找用户,Posts 通过 userId 查找用户的文章。我们的目标是获取所有用户的名称以及他们所写的文章的标题。

创建数据源

-- -------------------- ---- -------
----- ----- - -
  -- - --- -- ----- ---- --
  -- - --- -- ----- ----- --
  -- - --- -- ----- ------ -
-

----- ----- - -
  ---- - --- ---- ------ --------- ------- - --
  ---- - --- ---- ------ --------- ------- - --
  ---- - --- ---- ------ --------- ------- - --
  ---- - --- ---- ------ --------- ------- - --
  ---- - --- ---- ------ --------- ------- - --
  ---- - --- ---- ------ --------- ------- - -
-

-------- --------------- -
  ------ ---------
-

-------- ------------------------ -
  ------ --------------------
    ------------ -- ----------- --- -------
    ------------- ------ -- -------- - ---------
-

我们在数据源中存储了一些用户和他们写的文章。getUserById(id)getPostsByUserId(userId) 用于获取用户信息和用户的文章信息。

使用 DataLoader

接下来,我们将使用 DataLoader 来优化查询过程,避免 N+1 查询问题。下面是我们可以使用 DataLoader 解决这个问题的代码。

-- -------------------- ---- -------
----- ---------- - --- --------------- -- -----------------------------------
----- ----------- - --- --------------- -- ----------------------------------------

----- ----------------- - ----- -- -- -
  ----- ----- - ----- ----------------------- -- ---
  ----- ----- - ----- ----------------------------------- -- ---------
  ------ ---------------- ------ -- --
    --------
    ------ ------------
  ---
-

-------------------------------------

在上面的示例中,我们创建了两个 DataLoader 实例:userLoaderpostsLoaderuserLoader 用于加载所有的用户信息,postsLoader 用于加载所有用户的文章信息。

在查询过程中,我们首先使用 userLoader 加载所有用户的信息,然后使用 postsLoader 加载每个用户的文章信息。由于我们知道每个用户的 id,所以我们可以直接把用户 id 作为 postsLoader 的 keys。

最后,我们将用户的文章信息和用户信息组合成一个对象,并返回所有对象组成的数组。每个对象都包含用户信息和用户所写的文章的信息。

在使用 DataLoader 解决 N+1 查询问题时,需要进行一些实际测试,以了解在不同的情况下,程序的性能如何。

总结

在 GraphQL 中使用 DataLoader 可以解决 N+1 查询问题,提高查询性能。在本文中,我们介绍了 DataLoader 的使用方法,并提供了一个样例,演示了如何使用 DataLoader 解决 N+1 查询问题。

在实际使用中,我们需要不断试验以找到最好的解决方案。通过尝试不同的 Batch function 和 DataLoader options,我们可以优化我们的代码,并提高查询性能。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/648d0ef948841e9894b5a18a

纠错
反馈