我有一个存储在Neo4j中的树,在树中,每个节点都包含一个标识符和一个计数器。对于一堆节点,我希望能够以快速的方式递增该计数器。
所以我使用MERGE(使用ON CREATE SET和ON MATCH SET),但是性能很差。似乎如果我用两个事务来做,一个知道每个节点是否存在,另一个是创建它还是更新它,它会更快。
你知道为什么MERGE比MATCH和CREATE组合慢吗?我怎样才能提高其性能?
以下是您可以重现的相关示例:
import datetime
from py2neo import Graph
def bench(query, count, reset=True):
graph = Graph()
if reset:
graph.cypher.run("MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r")
graph.cypher.run("CREATE CONSTRAINT ON (node:Node) ASSERT node.number IS UNIQUE")
graph.cypher.run("CREATE (r:Root)")
start = datetime.datetime.now()
tx = graph.cypher.begin()
for i in range(count):
tx.append(query, {'i': i})
tx.commit()
print('---')
print('query : %s' % query)
print("%i create. %s/second." % (count, count // (datetime.datetime.now() - start).total_seconds()))
if __name__ == '__main__':
bench("MATCH (:Root)-[:Child]->(n:Node {number: {i}}) RETURN n.count", 1000)
bench("CREATE (:Root)-[:Child]->(:Node {number: {i}, count: 1})", 1000)
bench("MATCH (root:Root) CREATE (root)-[:Child]->(:Node {number: {i}, count: 1})", 1000)
bench("MATCH (root:Root) MERGE (root)-[:Child]->(n:Node {number: {i}}) ON CREATE SET n.count = 1 ON MATCH SET n.count = n.count + 1", 1000)
bench("MATCH (root:Root)-[:Child]->(n:Node {number: {i}}) SET n.count = n.count + 1", 1000)
bench("MATCH (root:Root) CREATE UNIQUE (root)-[:Child]->(n:Node {number: {i}}) SET n.count = coalesce(n.count, 0) + 1", 1000)
该代码的输出:
---
query : MATCH (:Root)-[:Child]->(n:Node {number: {i}}) RETURN n.count
1000 create. 1151.0/second.
---
query : CREATE (:Root)-[:Child]->(:Node {number: {i}, count: 1})
1000 create. 760.0/second.
---
query : MATCH (root:Root) CREATE (root)-[:Child]->(:Node {number: {i}, count: 1})
1000 create. 1092.0/second.
---
query : MATCH (root:Root) MERGE (root)-[:Child]->(n:Node {number: {i}}) ON CREATE SET n.count = 1 ON MATCH SET n.count = n.count + 1
1000 create. 218.0/second.
---
query : MATCH (root:Root)-[:Child]->(n:Node {number: {i}}) SET n.count = n.count + 1
1000 create. 3005.0/second.
---
query : MATCH (root:Root) CREATE UNIQUE (root)-[:Child]->(n:Node {number: {i}}) SET n.count = coalesce(n.count, 0) + 1
1000 create. 283.0/second.
感谢您的帮助:)
答案 0 :(得分:2)
MERGE和CREATE UNIQUE必须首先检查关系和结束节点,然后创建。
使用MERGE,如果您先创建子节点然后合并关系,那么您会更快。
只与一个绑定节点合并的变体将始终创建一个新的子节点!!
试试这个:
MATCH (root:Root)
MERGE (n:Node {number: {i}})
ON CREATE SET n.count = 1 ON MATCH SET n.count = n.count + 1
MERGE (root)-[:Child]->(n)
或者
MATCH (root:Root)
MERGE (n:Node {number: {i}})
ON CREATE SET n.count = 1 ON MATCH SET n.count = n.count + 1
CREATE UNIQUE (root)-[:Child]->(n)
答案 1 :(得分:1)
您可以在网络界面中通过PROFILE
或通过neo4j-shell仔细查看您的查询:http://neo4j.com/docs/stable/how-do-i-profile-a-query.html
这可能有助于了解MERGE
为何如此缓慢。
PROFILE MATCH (root:Root) MERGE (root)-[:Child]->(n:Node {number: 1})
ON CREATE SET n.count = 1 ON MATCH SET n.count = n.count + 1
看看MERGE
只有ON MATCH
的是否以及为什么慢于MATCH ... SET
会很有趣。也许cypher对两个查询都使用不同的索引,PROFILE
也告诉你这个。