在解析大数据时,从将解析器的结果返回给客户端的那一刻起,我注意到性能非常慢。
我假设apollo-server
遍历我的结果并检查类型……无论哪种方式,该操作都花费了很长时间。
在我的产品中,我必须一次全部返回大量数据,因为一次要使用它来在UI中绘制图表。我没有分页选项,可以在其中分割数据。
我怀疑速度缓慢来自apollo-server
,而不是我的解析器对象创建。
请注意,我记录了解析器创建对象所花费的时间,它的快速而不是瓶颈。
apollo-server
执行的稍后操作(我不知道如何测量)需要大量时间。
现在,我有一个版本,在该版本中,返回自定义标量类型JSON(响应)的速度要快得多。但是我真的更喜欢返回我的Series
类型。
我通过查看网络面板来测量两种类型(Series
和JSON
)之间的差异。
当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"
}
代码:
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}`);
});
游乐场的gql查询(针对Series系列):
query {
complex {
data {
name
values {
name
value
}
}
keys {
name
value
}
}
}
游乐场的gql查询(用于自定义标量类型JSON):
query {
complex
}
这是一个有效的示例:
https://codesandbox.io/s/apollo-server-performance-issue-i7fk7
任何潜在客户/想法都将受到高度赞赏!
答案 0 :(得分:3)
有一个相关的未解决问题here。 Lee Byron对此进行了很好的总结:
我认为此问题的TL; DR是GraphQL有一些开销,减少开销是不平凡的,完全删除它可能不是一个选择。归根结底,GraphQL.js仍然负责对返回数据的形状和类型进行API边界保证,并且根据设计,它不信任基础系统。换句话说,GraphQL.js进行运行时类型检查和子选择,这会花费一些成本。
GraphQL提供的好处(验证,子选择等)不可避免地会产生一些开销,因为它们需要对返回的数据进行额外的处理。不幸的是,这种开销随着数据的大小而扩展。我想如果您要实现一个支持部分响应并使用Swagger或Joi之类的响应验证的REST端点,就会遇到类似的问题。
“堆内存不足”错误的含义与所说的完全一样-您的堆内存不足。您可以尝试通过manually increasing the limit来缓解这种情况。
通常,应通过实现分页来分解此类大型数据集。如果这不是一种选择,那么使用custom scalar将是下一个最佳方法。这种方法的最大缺点是,使用您的API的客户端将无法请求您返回的JSON对象内的特定字段。在patching GraphQL.js之外,实际上没有其他选择可以加快响应速度并减少内存使用量。
答案 1 :(得分:0)
评论摘要
此数据结构/类型:
id
字段)无法正确规范化; 这种方式该数据集不是为graphQL设计的。当然,graphQL仍可用于获取此数据,但应禁用类型解析/匹配。
使用custom scalar types(graphql-type-json
)是一种解决方案。如果您需要某种混合解决方案-可以将Group.values
键入json(而不是整个Series
)。如果您要使用规范化的缓存[访问权限],则组仍应具有id
字段。
您可以使用apollo-link-rest
来获取“纯” json数据(文件),而将类型解析/匹配保留为仅客户端。
如果要使用一个graphql端点... 写自己的链接-使用指令-“询问json,输入”-上面两个的混合。就像在静止时一样,与解串器/序列化器链接。
在两种选择中-您为什么真正需要它?仅用于绘图?不值得的努力。没有分页,但希望流式传输(实时更新?)……没有游标……通过……上次更新加载更多(订阅/轮询)?可行,但“感觉不正确”。