mysql - 带有嵌套查询(Django + React)的 Graphql 瓶颈性能使前端应用程序无法使用。请帮忙 :'(
问题描述
对于这个项目,我在后端使用 Python+Django 和 GraphQL(石墨烯),MySQL 作为数据库,React.js 作为前端。
在前端,用户登录后,我要执行以下查询:
const GET_ORGANIZATION = gql`
query getOrganization($orgId : Int!) {
organization(id:$orgId){
id
name
user{
id
username
firstName
lastName
email
dateJoined
lastLogin
isActive
trainings {
id
name
sessions {
id
name
category
createdAt
totalSteps
completedAt
user {
id
}
eventSet {
id
category
description
object
errorSeverity
performedAt
}
}
}
}
courses{
id
name
description
trainings{
id
name
user{
id
username
isSuperuser
isStaff
isActive
email
}
sessions{
id
name
category
createdAt
completedAt
user{
id
username
}
eventSet {
id
category
description
object
errorSeverity
performedAt
}
}
}
}
}
}`;
如您所见,它嵌套了多个级别。当我进入会议和活动时,问题就来了。我不是 graphQL 的超级专家,但我一直认为 GraphQL 的卖点是您可以在一个查询中使用所有这些嵌套字段。好吧,事实并非如此。这里有几张图片为什么:
响应需要超过 30 秒。深入研究我的数据库的 slow_log,我发现:
使用相同的参数多次重复相同的查询:
这被重复超过 5000 次。通读 SO 和其他来源,这似乎是 GraphQL 的经典 N+1 问题
所以现在我面临(希望)解决方案的两条道路:
第一个,我找到了一种方法,使这个查询能够以现有的方式使用,有大量的数据,这就是我需要建议和帮助的地方。有办法吗?我想是这样想的,因为如果几个级别的嵌套和其中一个级别中的数千行足以使其无法使用,那么我想没有人会使用它。
第二种方式,这就是我今天开始做的方式,但后来停下来,哭着写了这篇文章:我限制我的查询只到培训级别,而不是有一个大查询,我有几个较小的查询。这种方法的问题是我痛苦的原因是我意识到我基本上必须在前端重做我的整个反应组件,因为他们中的许多人都希望数据在那个大对象中,更不用说什么时候您将该对象或其中的一部分传递给其他组件
你会建议哪一个?或者还有其他方法吗?请记住,我只有非常有限的时间来做出重大改变,因为在接下来的几天里,我的最后期限即将到来,让我有这个生活。
添加一些上下文: 我一个人在开发一种支持 VR 应用程序的度量应用程序。没有团队(我一加入就离开了前端和 devOps 人员),所以我必须做所有的 devOps、前端和后端工作,这是很多工作。由于这种压力,我犯了新手错误,从未花时间获取包含 VR 收集的所有数据的数据库副本,这些数据实际上是用户将在前端可视化的数据。所以我一直在用较小的虚拟数据进行测试,到目前为止一切都“完美”地工作。可能这对我自己和其他阅读此问题/寻求帮助的人来说是一个教训:确保您的环境在开发时尽可能接近生产,特别是有足够的数据
解决方案
这不是 GraphQL 本身的问题,而是石墨烯实现的问题。正如您所发现的,没有 SQL 查询优化,并且为每个深度级别创建查询,这对于深度 GraphQL 查询来说非常糟糕。
您拥有的一些选项:
通过为多个深度级别创建某种查询优化器来修复石墨烯的实现——根据您的分析,您可以看到进行一些改进很容易,如果您为开源项目做出贡献,社区将非常感激(但这个选项需要几个月的工作)
创建自己的解析器,劫持查询的特别慢的部分,并用您自己的优化查询替换,包括所有必要的连接并返回结构化的 JSON 输出
创建不属于 Django 架构的自定义字段和/或对象类型,并如上所述编写优化的字段解析器。
(更新)寻找查询优化器(有关详细信息,请参阅@Rafael 的答案)
推荐阅读
- jquery - 捕获水平和垂直滚动
- schema.org - Schema.org 类型的资源或下载列表
- ruby-on-rails - 使用 Gmail ruby API 的服务帐户添加转发地址
- java - 使用套接字将对象类从 java 服务器端正确发送到 android 客户端
- r - 如何将命名数字列表转换为具有基于列表名称的 id 的 data.frame?
- python - 使用 pytz 获取本地时区名称
- c++ - __vector_base_common 发生了什么?
- php - 如何检查来自一个foreach的值与另一个检查某些条件以显示数据
- elasticsearch - 使用 streaming_bulk() 时出现 409 错误 - 确定该文档仅包含一次。
- mongodb - MongoDB Java 驱动程序聚合字符串