Objection.js在哪里查询会减慢我的Node.js应用程序运行速度?

时间:2018-08-10 08:40:55

标签: node.js postgresql knex.js heroku-postgres objection.js

好,所以这基本上是是/否的答案。我有一个带有postgres计划的Heroku运行的节点应用程序。我使用Objection.js作为ORM。 我在大多数端点上都面临300 + ms的响应时间,而且我对这是为什么有一个理论。我想确认一下。

我的大多数api端点大约执行5-10次渴望的负载。 Objection.js通过执行其他WHERE IN查询而不是通过使用大量JOINS进行大型查询来处理急切的加载。这样做的原因是这种方式更容易构建,并且不会对性能造成太大损害。

但是,这让我开始思考:Heroku Postgres不能与我假设的节点应用程序在相同的heroku dyno上运行,因此这意味着每个查询都存在延迟。难道所有这些延迟加起来,导致总共300毫秒的延迟?

总结:如果您有单独托管的数据库,使用Knex而不是通过Objection.js生成查询会更快吗?

3 个答案:

答案 0 :(得分:1)

推测为什么会出现这种减速几乎是没有用的(无论如何我还是会推测一点;))。在这种情况下,要做的第一件事是测量使用300ms的位置。

在数据库中,如果有任何慢速查询引起问题,您应该能够查看查询时间。

在设置了DEBUG=knex:*环境变量的情况下,knex还会向控制台输出一些有关性能的信息。

现在,节点还具有内置的分析支持,您可以通过在启动节点时设置--inspect标志来启用该支持。然后,您将能够将您的节点进程与chrome dev工具连接起来,并查看节点在哪里花费时间。从该概要文件中,您将可以查看例如数据库查询结果解析是否在执行时间中占主导地位。

找出速度缓慢的最佳方法是隔离应用程序的缓慢部分,并仔细检查,甚至将该示例发布到stackoverflow上,其他人可以告诉您为什么它可能会缓慢。像这样的一般性解释并不能为其他工具提供太多帮助解决实际问题的工具。

  

Objection.js通过执行其他WHERE IN查询而不是通过使用大量JOINS进行大型查询来处理急切的加载。这样做的原因是这种方式更容易构建,并且不会对性能造成太大损害。

有异议,您可以选择要使用的热切算法。在大多数情况下(存在一对多或多对多关系时),与使用连接相比,进行多个查询实际上更有效,因为连接数量激增,传输时间+节点侧的结果解析将花费太多时间。

  

如果您有单独托管的数据库,那么使用Knex而不是通过Objection.js生成查询会更快吗?

通常不会。

推测部分:

  1. 您提到了Most of my api endpoints do around 5-10 eager loads.。在大多数情况下,我在查询中遇到这种缓慢情况的原因是应用程序正在从数据库查询太大的数据块。当查询返回例如数万行时,它将是几兆字节的JSON数据。仅将从数据库到JavaScript对象的大量数据解析需要数百毫秒。如果您的查询在这300毫秒内还导致CPU负载过高,则可能是您的问题。

  2. 查询速度慢。 Somtimes数据库的索引设置不正确,因此查询只需要线性扫描所有表即可获得结果。从数据库日志中检查慢查询将有助于找到这些查询。另外,如果获得响应的时间很长,但是节点进程的CPU负载很低,则可能是这种情况。

答案 1 :(得分:0)

在这里,我可以确认每个查询大约需要30ms,无论查询的复杂程度如何。由于负载急切,Objection.js实际上执行了大约10个单独的查询,解释了300ms的累积时间。


仅供参考;我现在走这条路⬇

我已经开始着手编写自己的更高级的SQL查询。看来您可以做一些非常高级的工作,获得与渴望加载Objection.js类似的结果

select 
  "product".*,
  json_agg(distinct brand) as brand,
  case when count(shop) = 0 then '[]' else json_agg(distinct shop) end as shops,
  case when count(category) = 0 then '[]' else json_agg(distinct category) end as categories,
  case when count(barcode) = 0 then '[]' else json_agg(distinct barcode.code) end as barcodes
from "product"
inner join "brand" on "product"."brand_id" = "brand"."id"
left join "product_shop" on "product"."id" = "product_shop"."product_id"
left join "shop" on "product_shop"."shop_code" = "shop"."code"
left join "product_category" on "product"."id" = "product_category"."product_id"
left join "category" on "product_category"."category_id" = "category"."id"
left join "barcode" on "product"."id" = "barcode"."product_id"
group by "product"."id"

这对1000种产品花费19ms,但是通常限制为25种产品,因此性能非常好。

答案 2 :(得分:0)

正如其他人所提到的,默认情况下,异议使用多个查询而不是联接来执行热切加载。这是比完全基于连接的加载更安全的默认设置,在某些情况下,加载可能会变得非常缓慢。您可以了解有关默认渴望算法here的更多信息。

您可以简单地通过调用joinEager而不是eager的方法来选择使用基于连接的算法。 joinEager执行一个查询。

对象还曾经具有(相当愚蠢的)默认值,即每个操作1个并行查询,这意味着eager调用内的所有查询都是顺序执行的。现在,该默认设置已被删除,即使在像您这样的情况下,它也应具有更好的性能。

您使用json_agg的技巧非常聪明,实际上避免了在某些情况下使用joinEager时可能出现的慢速问题。但是,这不能轻松地用于嵌套加载或与其他数据库引擎一起使用。