解决大数据时,Apollo Server的性能降低

ser*_*ner 5 graphql apollo-server

当解析大数据时,从将解析器的结果返回给客户端的那一刻起,我注意到性能非常低下。

我假设apollo-server对结果进行迭代并检查类型...无论哪种方式,该操作都花费了很长时间。

在我的产品中,我必须一次全部返回大量数据,因为一次要使用它来在UI中绘制图表。我没有分页选项,可以在其中分割数据。

我怀疑速度缓慢来自于apollo-server而不是解析器对象创建。

注意,我记录了解析器创建对象所花费的时间,它的快速而不是瓶颈。

apollo-server我不知道如何测量的稍后执行的操作会花费很多时间。

现在,我有了一个版本,在该版本中,返回自定义标量类型JSON(响应)的速度要快得多。但是我真的更喜欢返回我的Series类型。

我通过查看网络面板来测量这两种类型(SeriesJSON)之间的差异。

当AMOUNT设置为500且类型Series为时,大约需要1.5秒(即秒)

当AMOUNT设置为500且类型JSON为时,大约需要150毫秒(很快!)

当AMOUNT设置为1000且类型为时Series,它非常慢...

当AMOUNT设置为10000,类型为时,Series我的JavaScript堆内存不足(不幸的是,这是我们在产品中遇到的情况)


我还将apollo-server性能与进行express-graphql了比较,后者的运行速度更快,但仍不及返回自定义标量JSON的速度。

当AMOUNT设置为500时apollo-server,网络需要1.5秒

当AMOUNT设置为500时express-graphql,网络需要800毫秒

当AMOUNT设置为1000时apollo-server,网络需要5.4秒

当AMOUNT设置为1000时express-graphql,网络需要3.4秒


堆栈:

"dependencies": {
  "apollo-server": "^2.6.1",
  "graphql": "^14.3.1",
  "graphql-type-json": "^0.3.0",
  "lodash": "^4.17.11"
}
Run Code Online (Sandbox Code Playgroud)

代码:

const _ = require("lodash");
const { performance } = require("perf_hooks");
const { ApolloServer, gql } = require("apollo-server");
const GraphQLJSON = require('graphql-type-json');

// The GraphQL schema
const typeDefs = gql`
  scalar JSON

  type Unit {
    name: String!
    value: String!
  }

  type Group {
    name: String!
    values: [Unit!]!
  }

  type Series {
    data: [Group!]!
    keys: [Unit!]!
    hack: String
  }

  type Query {
    complex: Series
  }
`;

const AMOUNT = 500;

// A map of functions which return data for the schema.
const resolvers = {
  Query: {
    complex: () => {
      let before = performance.now();

      const result = {
        data: _.times(AMOUNT, () => ({
          name: "a",
          values: _.times(AMOUNT, () => (
            {
              name: "a",
              value: "a"
            }
          )),
        })),
        keys: _.times(AMOUNT, () => ({
          name: "a",
          value: "a"
        }))
      };

      let after = performance.now() - before;

      console.log("resolver took: ", after);

      return result
    }
  }
};

const server = new ApolloServer({
  typeDefs,
  resolvers: _.assign({ JSON: GraphQLJSON }, resolvers),
});

server.listen().then(({ url }) => {
  console.log(` Server ready at ${url}`);
});

Run Code Online (Sandbox Code Playgroud)

游乐场的gql查询(针对Series系列):

query {
  complex {
    data {
      name
      values {
        name
        value
      }
    }
    keys {
      name
      value
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

游乐场的gql查询(用于自定义标量类型JSON):

query {
  complex
}
Run Code Online (Sandbox Code Playgroud)

这是一个工作示例:

https://codesandbox.io/s/apollo-server-performance-issue-i7fk7

任何线索/想法将不胜感激!

Dan*_*den 14

有一个相关的悬而未决的问题在这里。Lee Byron 总结得很好:

我认为这个问题的 TL;DR 是 GraphQL 有一些开销,减少开销是非常重要的,完全删除它可能不是一个选择。最终 GraphQL.js 仍然负责对返回数据的形状和类型进行 API 边界保证,并且设计上不信任底层系统。换句话说,GraphQL.js 执行运行时类型检查和子选择,这需要一些成本。

GraphQL 提供的好处(验证、子选择等)不可避免地会产生一些开销,因为它们需要对您返回的数据进行额外处理。不幸的是,这种开销会随着数据的大小而扩展。我想如果您要实现支持部分响应并使用 Swagger 或 Joi 之类的东西进行响应验证的 REST 端点,您会遇到类似的问题。

“堆内存不足”错误的意思正是它所说的——堆上的内存不足。您可以尝试通过手动增加限制来缓解这种情况。

通常,像这样的大型数据集应该通过实现分页来分解。如果这不是一个选项,那么使用自定义标量将是下一个最佳方法。这种方法的最大缺点是使用您的 API 的客户端将无法请求您返回的 JSON 对象中的特定字段。除了修补 GraphQL.js之外,真的没有其他选择可以加快响应速度并减少内存使用量。