什么是 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 的一般步骤如下:
- 加载依赖
----- ---------- - ---------------------
- 创建 Batch function
Batch function 是一个用于将 keys 数组转换为对应的值数组以及从数据源中获取数据的函数。
下面是一个最简单的 Batch function 的实现:
-------- ------------------- - ------ ---------------------------- -- ----------- -
batchFunction
接收一个 keys 数组作为参数,返回一个 Promise,Promise 返回的数据是对应 keys 的值数组。在上面的例子中,我们假设 data
是一个全局对象,用来存储从数据源中获取的数据。根据处理过程中实际情况,batch function 的实现可能会有所不同。
- 创建 DataLoader
使用 Batch function 创建 DataLoader:
----- ------ - --- -------------------------
这个 DataLoader 的 Batch function 是 batchFunction
,当使用这个 DataLoader 时,它会将 keys 数组作为 Batch function 的参数,然后返回对应的值数组。例如:
-------------------------------- -- -- - ----
如果一次性要查询多个 key,可以使用 loadMany
方法:
------------------- -- --------------------- -- -- --- -- -- ------
- 缓存 DataLoader
在 DataLoader 中使用缓存可以避免重复请求相同的数据。
----- ----------- - --- ----------- -------------- -- ----- -------- - ------ ---- - -- ---------- ------- -
在创建 DataLoader 时,可选的选项 cache
指示 DataLoader 是否要缓存数据。如果设置为 true,则 DataLoader 会使用 LRU 缓存算法来缓存数据。查询相同的 key 时,会直接从缓存中获取数据,而不是从数据源中获取。
- 并发 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 实例:userLoader
和 postsLoader
。userLoader
用于加载所有的用户信息,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