解决 GraphQL 中的 N+1 查询问题

阅读时长 8 分钟读完

在GraphQL中,N+1查询问题是一个常见的问题,如果没有解决好,会导致性能问题。本文将介绍什么是N+1查询问题、为什么会出现这个问题以及如何解决它。

什么是N+1查询问题

当我们使用GraphQL查询数据时,有时我们需要查询一个包含关系的对象。例如,查询一个用户的所有博客:

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

GraphQL会将以上查询转换为两个SQL查询:

但是,当我们查询多个用户的所有博客时,通常会使用user(id: [1, 2, 3])这样的语句,GraphQL会将以上查询转换为N个SQL查询,每一个用户对应一个SQL查询:

这就是N+1查询问题。在这个例子中,我们需要执行N+1个查询,其中N是用户的数量。

当我们只有几个用户时,这并不是一个大问题,但是当我们有成千上万的用户时,查询的性能就会受到严重的影响。

为什么会出现N+1查询问题

N+1查询问题的根源在于GraphQL的查询语法。当我们查询一个包含关系的对象时,我们需要使用GraphQL的嵌套语法。例如,查询一个博客的作者:

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

在这个查询中,我们要获取一个博客的作者。GraphQL需要查询两个表:blogs和users。 如果我们查询多个博客的作者,就会发生N+1查询问题。

如何解决N+1查询问题

下面介绍两种解决N+1查询问题的方法。

手动查询

最简单的解决办法是手动查询所有数据。 当我们使用GraphQL查询数据时,可以触发一个函数来手动查询数据。 我们可以查询所有包含关系的数据,这样就可以避免N+1查询问题。

例如,使用dataloader模块加载数据:

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

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

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

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

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

在上面的例子中,我们使用dataloader模块来加载所有关系数据。 在loaders对象上创建一个新的DataLoader实例,并提供一个函数来加载所有数据。在解析器函数中,我们使用loaders对象来查询数据。

手动查询方法的优点是简单易懂,可以解决几乎所有的N+1查询问题。 缺点是代码冗长,需要手动编写每个关系的解析器函数。

使用批处理器

另一种解决N+1查询问题的方法是使用批处理器。 批处理器是一个库,可以批量查询数据。我们可以使用一个批处理器来查询所有数据,并将结果返回给GraphQL。

例如,使用dataloader模块和batch-sql模块批量查询数据:

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

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

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

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

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

在上面的例子中,我们使用batch-sql模块和dataloader模块来批量查询所有关系数据。在batch-sql模块中,我们可以发送一个SQL查询,因为查询的参数是一个数组,所以我们可以批量查询所有数据。当查询完成时,我们将结果缓存起来,并在DataLoaderload方法中返回结果。

批处理器的优点是它可以自动批量查询所有数据,因此不需要手动编写查询函数,使代码更简洁。 缺点是需要使用额外的批处理器库,并且可能需要学习一些新的概念。

结论

在GraphQL中,N+1查询问题是一个常见的性能问题。 本文介绍了什么是N+1查询问题,为什么会出现这个问题,以及如何解决它。 通过使用手动查询或批处理器,我们可以避免N+1查询问题并提高性能。

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

纠错
反馈