2015-05-22 3 views
1

У меня есть дерево, которое я храню в Neo4j, в дереве каждый узел содержит идентификатор и счетчик. Я хочу, чтобы ускорить этот счетчик быстрым способом, для множества узлов.Neo4j merge performance VS create/set

Так что я использовал 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. 

Спасибо за вашу помощь :)

ответ

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 также рассказывает об этом.

0

И 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)