数据加载器(Dataloader)
由于 GraphQL 的灵活性,当我们加载某个对象的关联对象时,我们通常需要执行多个查询。 这就造成了著名的 N+1 查询问题。为了解决这个问题,我们可以使用 DataLoader。
DataLoader
能够将多个请求合并为一个请求,从而减少数据库的查询次数,同时还能缓存查询结果,避免重复查询。
示例
表格定义
考虑我们有两张表 users
和 posts
,其中 posts
通过 posts.authorId
关联到 users
的 id
:
数据填充
让我们使用 drizzle-seed 为数据库填充一些数据:
创建解析器
让我们为 User
对象编写一个简单的解析器:
在上面的代码中,我们定义了一个用户解析器,它包含:
users
查询:用于获取所有用户posts
字段:用于获取对应用户的所有帖子
下面是一个示例查询,它将返回所有用户的信息以及对应的帖子:
这个查询将为每个用户分别查询他们的帖子。我们在前一步在数据库里填充了 20 个用户,所以这个查询将引起 20 次对 posts
表的查询。
这显然是一种非常低效的方式,让我们来看看如何使用 DataLoader 来减少查询次数。
使用 DataLoader
接下来,我们将使用 DataLoader 来优化我们的查询。
在上面的代码中,我们使用 field().load()
来启用数据批量加载,在幕后这将使用 DataLoader
来批量加载数据。
在 load()
内部,我们通过以下步骤实现数据批量加载:
- 使用
in
操作从posts
表中一次性获取所有当前加载的用户的帖子; - 使用
Map.groupBy()
将帖子列表按作者 ID 分组; - 将用户列表按顺序映射到帖子列表,如果某个用户没有帖子,则返回一个空数组。
如此一来,我们将原先的 20 次查询合并为 1 次查询,从而实现了性能优化。
必须保证查询函数的返回数组顺序与 IDs
数组顺序一致。DataLoader
依赖于此顺序来正确地合并结果。