在GraphQL中,N+1查询问题是一个常见的问题,如果没有解决好,会导致性能问题。本文将介绍什么是N+1查询问题、为什么会出现这个问题以及如何解决它。
什么是N+1查询问题
当我们使用GraphQL查询数据时,有时我们需要查询一个包含关系的对象。例如,查询一个用户的所有博客:
-- -------------------- ---- ------- - -------- ---- - ---- ----- - ----- ------- - - -
GraphQL会将以上查询转换为两个SQL查询:
SELECT * FROM users WHERE id = 1; SELECT * FROM blogs WHERE user_id = 1;
但是,当我们查询多个用户的所有博客时,通常会使用user(id: [1, 2, 3])
这样的语句,GraphQL会将以上查询转换为N个SQL查询,每一个用户对应一个SQL查询:
SELECT * FROM users WHERE id = 1; SELECT * FROM blogs WHERE user_id = 1; SELECT * FROM users WHERE id = 2; SELECT * FROM blogs WHERE user_id = 2; SELECT * FROM users WHERE id = 3; SELECT * FROM blogs WHERE user_id = 3;
这就是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查询,因为查询的参数是一个数组,所以我们可以批量查询所有数据。当查询完成时,我们将结果缓存起来,并在DataLoader
的load
方法中返回结果。
批处理器的优点是它可以自动批量查询所有数据,因此不需要手动编写查询函数,使代码更简洁。 缺点是需要使用额外的批处理器库,并且可能需要学习一些新的概念。
结论
在GraphQL中,N+1查询问题是一个常见的性能问题。 本文介绍了什么是N+1查询问题,为什么会出现这个问题,以及如何解决它。 通过使用手动查询或批处理器,我们可以避免N+1查询问题并提高性能。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/674c92e3a336082f25427117