浅谈GraphQL-to-SQL

2020-05-22 21:28:46

GraphQL以其N+1问题而著称,这一问题在幼稚的实现中经常会发生。这导致我们很多人试图以最有效的方式使用GraphQL来解决数据获取问题。除了流行的Dataloader方法之外,解决此问题的另一种非常常见的方法是采用GraphQL查询,并提出解决该问题所需的确切SQL:

//JOIN Monster示例{SELECT USER(id:1){";USER";。";id";,id编码";用户";。";First_Name";,fullName==>;";用户";。";LAST_NAME";,电子邮件";用户";。";电子邮件地址";}来自";帐户";用户";},其中";用户";。";id";=1。

看一下这个查询,这绝对是获取它的数据的最有效的方式。这是一个非常有趣的想法,难怪很多人都想要这样的👇。

Hasura有一个基于PostgreSQL的强大的GraphQL to SQL引擎,可以将GraphQL查询编译成SQL语句。

PostGraphile";将任意深度的查询树编译成单个SQL语句,从而实现极高的执行效率。

所有这些工具都略有不同,它们解决的核心问题也不尽相同。然而,它们都在很大程度上依赖于将GraphQL AST编译成SQL语句,而SQL语句有望比从简单的GraphQL实现执行的组合SQL语句性能更高。我看到在这些工具上做了很多努力,但它们似乎从未真正能够解决我每天看到的GraphQL数据获取问题。

Hasura和PostGraphile是真正可靠的产品,不同于仅仅是GraphQL到SQL的产品,因为它们的目标是成为构建应用程序的完整引擎。在这两个工具中,数据库驱动了许多应用程序逻辑,尽管Hasura允许您使用数据库支持的模式缝合您自己的自定义解析器,PostGraphile则使用PostgreSQL函数处理自定义用例。

老实说,这两个工具都非常适合获取即时GraphQL API,特别是当您不想维护自己的GraphQL服务器时。这就是这里的权衡。然而,对于对提供GraphQL界面感兴趣的现有服务器应用程序来说,这并不是最好的选择。超级图表也是如此。它获取现有数据库,对其进行内部检查,并为其生成完整的GraphQL模式,包括排序和排序。如果您想要快速搭建一些东西,那么它很有用,但是对于数据库之外具有大量逻辑的大型现有代码库来说,它肯定没有那么大的用处。

其他工具,如Join Monster和SqlMancer,采取了略有不同的方法。他们不一定需要内省现有的数据库,将GraphQL模式定义与数据库模式定义分离。

像Join Monster(和一定程度上的Prisma工具)这样的工具需要以某种方式查看GraphQL查询,并决定从数据库获取什么。

常量用户=new({name:';user';,sqlTable:';Accounts';Unique eKey:';id';,field:()=>;({/*.*/})})。

这些工具的最大障碍是超出了GraphQL字段和SQL列之间的1:1映射。例如,您将如何处理GraphQL字段名,它实际上依赖于连接FirstName和LastName的逻辑?要神奇地实现这一点是非常困难的,因此,例如加入Monster可以让您配置此行为:

常量用户=新建({//.。字段:()=>;({fullName:{description:';,用户的名字和姓氏';,type:GraphQLString,//可能没有字段到列的一对一映射//此字段依赖于多列sqlDep:[';First_Name';,';Last_Name';],解析:User=>;`${user.first_name}${user.last_name}`}})。

Join Monster API附带了相当多的限制。我们的类型必须映射到数据库表,并且我们必须知道字段的SQL依赖关系。想象一下GitHub的GraphQL API上的字段PullRequest.mergeable。我们可以想象这个字段可能需要加载来自GIT的数据,来自在PullRequest上运行的各种检查的数据,来自不同Pull请求审查的数据。这不仅是因为它已经不止一个表了,而且它很可能会随着需要更多的数据而不断发展。这使得很难设计出不脆弱的sqlDep配置。

对于像GitHub这样复杂的API,或者任何真正大型的应用程序来说,要提供准确的数据库需求来满足用例是相当困难的。有时,数据被加载到现有逻辑的深处,并封装在条件条件中。不仅如此,这也没有考虑到应用程序缓存,以及调用其他数据源或其他服务的字段。不要误会我的意思,这对于懒惰的加载方法来说也是非常具有挑战性的。但至少我们不必在接口层维护SQL的配置,这在我看来确实更好。

当心那些让这件事看起来太容易的工具。加入Monster确实很擅长它的工作,但正如您所见,需要复杂的配置才能让事情在快乐的道路之外工作。如果该库没有这些配置并且只依赖于命名约定,要么它是从数据库模式自动生成的GraphQL API,要么它可能太天真,无法处理更复杂的情况(或者它可能有一些人工智能来分析您的数据依赖关系,谁知道🙊)。

生成一个巨大的SQL语句总是比生成几个较小的SQL语句快吗?这远不是一个新问题,显然很难像这样笼统地回答。然而,我们可以肯定地说,情况并不总是如此。对于这样的问题,我总是阅读High Performance MySQL。我不想在这里破坏这本书,但它确实突出了许多原因,为什么大型SQL查询并不总是更好的选择。很多应用程序通常需要尝试加入分解。这为我们提供了一些潜在的优势:

一些GraphQL-to-SQL工具可能足够聪明,知道如何做到这一点,但这肯定比优化数据加载器更像是一个黑匣子。总而言之,数据库性能并不总是像创建一条大型SQL语句那样简单。

虽然我不建议将这些工具中的大多数提供给那些拥有复杂的现有代码库的人,但我确实认为GraphQL执行还有很大的改进空间。虽然GraphQL与数据存储无关,但它的执行性质确实对我们如何构建应用程序逻辑施加了某些约束。理想情况下,事情会很顺利的,TM。Prisma客户在这方面是令人兴奋的,如果它继续变得越来越好的话。也许我们将看到的另一种解决方案是某种代理,就像ProxySQL,应用程序可以使用数据库而不用担心GraphQL的执行,而且不知何故,代理可以理解查询并实现高性能的查询。

如果您对引导一个新的API感兴趣,并且您对以数据库服务器为核心感到满意,我只能推荐Hasura和Postgraph,它们都很棒。对于现有的代码库,尤其是大型/复杂的代码库,我坚持使用Dataloader/Lazy/Achronous Load方法作为GraphQLAPI的加载方法,而不是使用旨在提前生成SQL的工具。

如果您在现有的复杂代码库上使用过其中一些工具,我将很高兴收到您的来信。

如果你喜欢这篇文章,你可能会喜欢我刚刚发布的生产准备好的GraphQL书!