GraphQL性能问题及简易解决方案
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
GraphQL最大的缺陷之一是缺少一些REST中常见的、对应用程序性能至关重要的基本实现。一个简单的GraphQL服务器没有内置缓存或批处理机制,这可能会给你的应用程序带来一些问题。
重复通话……很多重复通话
GraphQL 用于查询对象上的特定字段。这些查询fields语句旨在作为独立函数运行,这意味着不够完善的 GraphQL 服务器可能会在每次field解析查询语句时都发出冗余的数据库请求。
我们以一个简单的GraphQL 博客为例。我们的博客将使用分页来加快加载速度,假设我们想一次显示 5 篇文章。
我们希望获取文章及其作者姓名。作者信息是一个标准user字段,有自己的解析器,因此我们需要:
- 1. 请求提供我们博客文章的列表
- 需要调用 5 次才能获取每篇文章的作者信息(我们需要为每个解析器获取一个用户)。
但如果这些帖子有评论呢?假设每篇博文有 2 条评论,每条评论当然也有作者,那么结果如下:
- 1. 请求提供博客文章列表
- 每篇文章都发出5次作者征集通知。
- 调用 5 次命令获取每篇文章的评论列表
- 打了10个电话才联系到评论作者
这样一来,我们就需要调用 21 个函数。如果每篇文章下面有 10 条或 20 条评论呢?调用次数将会激增,这将严重影响应用程序的加载速度。
批处理和缓存
幸运的是,这个问题有解决办法!DataLoader是 Facebook 开发团队开发的一个实用工具,它可以让你批量处理和缓存数据库调用。
批量处理是 DataLoader 的主要功能。当 DataLoader 检测到您多次访问同一个表时,它会将所有调用合并在一起,例如,5 篇博客文章的作者信息和 10 条评论的作者信息将被合并到一个调用中。
首先,你需要通过提供批量加载功能来创建加载器。
var DataLoader = require('dataloader')
var userLoader = new DataLoader(keys => myBatchGetUsers(keys));
该函数接受一个键数组,并返回一个 Promise,该 Promise 会解析为一个值数组。您需要从数据加载器加载各个值。DataLoader 会将同时发生的单个加载操作合并,然后使用所有请求的键调用您的批处理函数。
userLoader.load(1)
.then(user => userLoader.load(user.invitedByID))
.then(invitedBy => console.log(`User 1 was invited by ${invitedBy}`));
// Elsewhere in your application
userLoader.load(2)
.then(user => userLoader.load(user.lastInvitedID))
.then(lastInvited => console.log(`User 2 last invited ${lastInvited}`));
缓存是 DataLoader 的另一项功能。每个DataLoader实例代表一个唯一的缓存,当 DataLoader 检测到两篇博客文章、评论或它们的组合具有相同的作者时,它不会进行新的数据库调用,而是会重用内存中已有的对象。
如果您想确保应用程序尽可能流畅地运行,我强烈建议您尝试使用 DataLoader。要了解更多关于 DataLoader 实现的信息,请查看其GitHub 代码库,或观看GraphQL 创建者之一Lee Byron制作的“DataLoader - 源代码演示”视频:
