我有以下查询,这很慢:
MATCH (defn :WORKFLOW_DEFINITION { uid: '48a72b6b-6791-4da9-8d8f-dc4375d0fe2d' }),
(instance :WORKFLOW_INSTANCE :SUCCEEDED) -[:DEFINED_BY]-> (defn)
WITH instance, defn
ORDER BY instance.createdAt, instance.uid
LIMIT 1000
OPTIONAL MATCH (instance) -[:INPUT]-> (input :ARTIFACT) <-[:DEFINES_INPUT]- (inputDefn :WORKFLOW_ARTIFACT) <-[:TAKES_INPUT]- (defn)
WITH instance, defn,
collect([input.uid, inputDefn.label, input.bucket, input.key, input.blobUid]) AS inputs
RETURN instance, defn, inputs
我的数据结构如下:
我目前有少量的WORKFLOW_DEFINITIONS。大多数WORKFLOW_DEFINITIONs都有一些WORKFLOW_INSTANCE,但是其中一个(我感兴趣的查询)有〜2000 WORKFLOW_INSTANCE。该定义包含三个WORKFLOW_ARTIFACT,因此,此定义的每个WORKFLOW_INSTANCE都有三个与WORKFLOW_ARTIFACT链接的ARTIFACT。
当我运行上面的查询时,它总共需要50449598个数据库命中,并且需要16654毫秒。
另一方面,下面的查询仅在第6行的末尾省略了'<-[:TAKES_INPUT]-(defn)',总共仅需要55598次db hit,耗时82毫秒。这是省略了该位的快速查询:
MATCH (defn :WORKFLOW_DEFINITION { uid: '48a72b6b-6791-4da9-8d8f-dc4375d0fe2d' }),
(instance :WORKFLOW_INSTANCE :SUCCEEDED) -[:DEFINED_BY]-> (defn)
WITH instance, defn
ORDER BY instance.createdAt, instance.uid
LIMIT 1000
OPTIONAL MATCH (instance) -[:INPUT]-> (input :ARTIFACT) <-[:DEFINES_INPUT]- (inputDefn :WORKFLOW_ARTIFACT)
WITH instance, defn,
collect([input.uid, inputDefn.label, input.bucket, input.key, input.blobUid]) AS inputs
RETURN instance, defn, inputs
以下是速度缓慢的概要分析查询计划: Slow query plan
并快速: Fast query plan
为什么最后一个边缘回到WORKFLOW_DEFINITION(以确保我们得到正确的WORKFLOW_ARTIFACT,因为ARTIFACT可能用于不同的工作流中)使查询如此缓慢?这个看起来像组合爆炸的数据库命中数是哪里来的?
在此先感谢您的帮助,如果您还有其他需要说明的地方,请告诉我!
答案 0 :(得分:0)
看看这两个查询计划,查询计划处理扩展的方式有所不同。
如果您查看较慢的查询计划中的操作流程,则会发现它从defn
扩展到inputDefn
,然后从inputDefn
扩展到input
,最后是从instance
到input
的Expand(Into)。
速度更快的查询在开始处理右侧时不会考虑defn
(因为它不参与OPTIONAL MATCH的任何扩展),因此不能使用相同的方法。相反,它从另一个方向扩展:从instance
扩展到input
,然后从input
扩展到inputDefn
。看起来使用该方向进行遍历最终会更加高效。
要点是,从input
到inputDefn
的遍历是有效的,它看起来是1-1相关。但是,inputDefn
到input
似乎是一个可怕的扩展,看起来inputDefn
往往是超节点,每个与input
节点的关系平均为2400。
关于如何解决此问题,虽然没有计划者提示明确地避免朝某个方向扩展,但我们也许可以使用join hints来推动计划者实现这一目标。 (您可以尝试对不同变量使用联接提示,并解释计划以确保避免将inputDefn
扩展为input
)。
如果我们添加以下提示:USING JOIN ON inputDefn
,则意味着我们将从双方向inputDefn
扩展并进行散列连接以获得最终结果集。这样可以避免代价高昂的从inputDefn
到input
的扩展。
MATCH (defn :WORKFLOW_DEFINITION { uid: '48a72b6b-6791-4da9-8d8f-dc4375d0fe2d' }),
(instance :WORKFLOW_INSTANCE :SUCCEEDED) -[:DEFINED_BY]-> (defn)
WITH instance, defn
ORDER BY instance.createdAt, instance.uid
LIMIT 1000
OPTIONAL MATCH (instance) -[:INPUT]-> (input :ARTIFACT) <-[:DEFINES_INPUT]- (inputDefn :WORKFLOW_ARTIFACT) <-[:TAKES_INPUT]- (defn)
USING JOIN ON inputDefn
WITH instance, defn,
collect([input.uid, inputDefn.label, input.bucket, input.key, input.blobUid]) AS inputs
RETURN instance, defn, inputs