我的数据模型
(:Parent {parentId:...})-[:CONTAINS]->(:Child)-[:SUPPORTS]->(:Dummy)
每个Child
只有一个Parent
。Parent.parentId
属性是唯一的,即有一个约束定义为
CREATE CONSTRAINT Parent_parentId_unique IF NOT EXISTS ON (p:Parent) ASSERT p.parentId IS UNIQUE;
我有一个用户定义的@Procedure
,它接受parentIds
的集合,我想从它们的所有子对象中删除所有:SUPPORTS
关系。
- 当一切都是加密的时候,过程的执行是缓慢的--几百毫秒到几秒。
Result removeRelationshipResult = tx.execute(""
+ "MATCH (p:Parent)-[:CONTAINS]->(c:Child)-[r:SUPPORTS]->(:Dummy)\n"
+ "WHERE p.parentId IN $parentIds\n"
+ "DELETE r",
Map.of("parentIds", parentIds)
);
- 当我以编程方式遍历所有关系时,执行速度很快--不到10毫秒(
streamOf
是utility method to convert Iterable to Stream)。
RelationshipType CONTAINS = RelationshipType.withName("CONTAINS");
RelationshipType SUPPORTS = RelationshipType.withName("SUPPORTS");
for (Long parentId : parentIds) {
Node parentNode = tx.findNode(Parent.LABEL, "parentId", parentId);
streamOf(parentNode.getRelationships(Direction.OUTGOING, CONTAINS))
.map(rel -> rel.getEndNode())
.flatMap(childNode -> streamOf(childNode.getRelationships(SUPPORTS)))
.forEach(Relationship::delete);
}
即使在没有:SUPPORTS
关系的情况下,第一次尝试也会出现差异。
造成这种差异的原因是什么?如何发现它?
**更新:**对@cybersam的回答的React(太长,无法评论):
我在样本上测试了你的建议,使用了1737个Parent
节点和655344个:SUPPORTS
关系,分成61个批次(用于从第2点开始的预热,尽管拆分的主要目的不同)。
应用点#1导致了巨大的性能改进。现在的时间与方案执行时间相当。我还试图改变程序实现,反之亦然,即。添加过滤到节点标签,但没有显著效果。实际上,第一次运行(当不存在关系时)和第二次运行(当实际删除第一次运行的关系时)的时间比较不同。第三次和下一次运行与第二次运行相似。第1点清楚地回答了我的问题,并帮助了我很多,谢谢!
| 实施|首次运行:删除时间/总手术时间|第二次运行:删除时间/总手术时间[ms]|
| --------------|--------------|--------------|
| 密码标记|79366/131261|170283/188783|
| 密码非标记|230/13756|1800/17284|
| 程序标签|155/11731|2235/19539|
| 程序无标签|174/11805|2079/19111|
1条答案
按热度按时间3htmauhk1#
造成速度差异的原因至少有三个。
1.您的Cypher查询指定
SUPPORT
关系的起始节点必须是Child
,其结束节点必须是Dummy
。您的Java代码不关心这两个节点有什么标签,因此必须做更少的工作。这个替代的MATCH
子句更等效:1.当第一次执行Cypher查询时(或者在从Cypher查询缓存中删除后再次执行),必须解析、检查错误并转换为基于DB的当前特征进行优化的Java操作--所有这些都需要时间。一旦Cypher查询的操作被缓存,相同的Cypher查询将运行得更快。因此,更公平的比较要求您多次运行Cypher查询,并忽略第一次运行的时间。
1.与生成代码的情况一样,您不能期望从Cypher查询生成的Java代码总是与尝试做同样事情的手工代码一样高效。一些额外的开销是正常的。