当您执行深度嵌套的 GraphQL 查询时,会调用多少 SQL 次数据库?

How many SQL database calls are made when you do a deeply nested GraphQL query?

我知道您要使用 GraphQL 为查询实现 backend handlers。所以如果你使用的是 PostgreSQL,你可能会有这样的查询:

query {
  authors {
    id
    name
    posts {
      id
      title
      comments {
        id
        body
        author {
          id
          name
        }
      }
    }
  }
}

天真的解决方案是做这样的事情:

const resolvers = {
  Query: {
    authors: () => {
      // somewhat realistic sql pseudocode
      return knex('authors').select('*')
    },
  },
  Author: {
    posts: (author) => {
      return knex('posts').where('author_id', author.id)
    },
  },
  Post: {
    comments: (post) => {
      return knex('comments').where('post_id', post.id)
    },
  },
};

然而,这将是一个相当大的问题。它基本上会执行以下操作:

  1. 对所有作者进行 1 次查询。
  2. 对于每个作者,查询所有 posts。 (n + 1 个查询)
  3. 对于每个 post,查询所有评论。 (n + 1 个查询)

所以这就像一个扇形查询。如果有 20 个作者,每个作者有 20 个 posts,那将是 21 个数据库调用。如果每个 post 有 20 条评论,那将是 401 次数据库调用! 20 位作者解决了 400 posts,这解决了 8000 条评论,这并不是你真正会这样做的方式,而是为了证明这一点。 1 -> 20 -> 400 个分贝调用。

如果我们添加 comments.author 次调用,那就是另外 8000 次数据库调用(每个评论一次)!

你如何将其批量化为 3 个数据库调用(每种类型 1 个)?这就是优化的 GraphQL 查询解析器本质上所做的吗?或者对于这种情况最好的办法是什么?

这是 GraphQL N+1 加载问题。

基本上有两种方法可以解决(为简单起见,假设它只需要加载作者及其 posts)

  1. 使用Dataloader模式。基本上它的想法是将每个作者的 posts 的实际加载时间推迟到特定时间,以便 N 个作者的 posts 可以由单个 SQL 一起批量加载.它还提供了缓存功能,以进一步提高同一请求的性能。

  2. 使用“前瞻模式”(here 中描述了一个 Java 示例)。基本上它的想法是,在解析 authors 时,您只需向前看查询是否在子字段中包含 posts。如果是,则您可以使用 SQL 联接将作者及其 post 集中在单个 SQL.

此外,为了防止恶意客户端发出检索非常大的图的请求,一些 GraphQL 服务器将分析查询并对其施加 depth limit