Neo4j 中性能缓慢的批量更新关系属性
Slow performance bulk updating relationship properties in Neo4j
我正在努力有效地批量更新 Neo4j 中的关系属性。 objective 是更新约 500,000 个关系(每个关系大约有 3 个属性),我将这些关系分成 1,000 个批次并在单个 Cypher 语句中处理,
UNWIND {rows} AS row
MATCH (s:Entity) WHERE s.uuid = row.source
MATCH (t:Entity) WHERE t.uuid = row.target
MATCH (s)-[r:CONSUMED]->(t)
SET r += row.properties
然而,每批 1,000 个节点大约需要 60 秒。 :Entity
标签的 UUID 属性 上存在一个索引,即我之前 运行、
CREATE INDEX ON :Entity(uuid)
这意味着根据查询计划匹配关系非常高效,
总共有 6 次数据库命中,查询在大约 150 毫秒内执行。我还在 UUID 属性 上添加了唯一性约束,确保每个匹配仅 returns 一个元素,
CREATE CONSTRAINT ON (n:Entity) ASSERT n.uuid IS UNIQUE
有谁知道我该如何进一步调试它以了解为什么 Neo4j 需要这么长时间来处理这些关系?
请注意,我正在使用类似的逻辑来更新节点,这些节点的速度提高了几个数量级,并且具有更多与之关联的元数据。
作为参考,我使用的是 Neo4j 3.0.3、py2neo 和 Bolt。 Python 代码块的形式为,
for chunk in chunker(relationships): # 1,000 relationships per chunk
with graph.begin() as tx:
statement = """
UNWIND {rows} AS row
MATCH (s:Entity) WHERE s.uuid = row.source
MATCH (t:Entity) WHERE t.uuid = row.target
MATCH (s)-[r:CONSUMED]->(t)
SET r += row.properties
"""
rows = []
for rel in chunk:
rows.append({
'properties': dict(rel),
'source': rel.start_node()['uuid'],
'target': rel.end_node()['uuid'],
})
tx.run(statement, rows=rows)
试试这个查询:
UNWIND {rows} AS row
WITH row.source as source, row.target as target, row
MATCH (s:Entity {uuid:source})
USING INDEX s:Entity(uuid)
WITH * WHERE true
MATCH (t:Entity {uuid:target})
USING INDEX t:Entity(uuid)
MATCH (s)-[r:CONSUMED]->(t)
SET r += row.properties;
它使用查询计划中显示的 index hints to force an index lookup for both Entity
nodes and then an Expand(Into)
operator which should be more performant than the Expand(All)
和 Filter
运算符。
@william-lyon 我想知道我是否需要 WITH * WHERE true
子句?我问的原因是DB命中数从4增加到8,即
PROFILE
MATCH (s:Entity {uuid:row.source})
USING INDEX s:Entity(uuid)
MATCH (t:Entity {uuid:row.target})
USING INDEX t:Entity(uuid)
MATCH (s)-[r:CONSUMED]->(t)
returns
而
PROFILE
MATCH (s:Entity {uuid:row.source})
USING INDEX s:Entity(uuid)
WITH * WHERE true
MATCH (t:Entity {uuid:row.target})
USING INDEX t:Entity(uuid)
MATCH (s)-[r:CONSUMED]->(t)
returns
请注意,使用索引提示可将数据库命中数从 6 减少到 4。对于上下文,我们有多个节点标签(和索引),尽管每个节点都有 :Entity
标签。
我正在努力有效地批量更新 Neo4j 中的关系属性。 objective 是更新约 500,000 个关系(每个关系大约有 3 个属性),我将这些关系分成 1,000 个批次并在单个 Cypher 语句中处理,
UNWIND {rows} AS row
MATCH (s:Entity) WHERE s.uuid = row.source
MATCH (t:Entity) WHERE t.uuid = row.target
MATCH (s)-[r:CONSUMED]->(t)
SET r += row.properties
然而,每批 1,000 个节点大约需要 60 秒。 :Entity
标签的 UUID 属性 上存在一个索引,即我之前 运行、
CREATE INDEX ON :Entity(uuid)
这意味着根据查询计划匹配关系非常高效,
总共有 6 次数据库命中,查询在大约 150 毫秒内执行。我还在 UUID 属性 上添加了唯一性约束,确保每个匹配仅 returns 一个元素,
CREATE CONSTRAINT ON (n:Entity) ASSERT n.uuid IS UNIQUE
有谁知道我该如何进一步调试它以了解为什么 Neo4j 需要这么长时间来处理这些关系?
请注意,我正在使用类似的逻辑来更新节点,这些节点的速度提高了几个数量级,并且具有更多与之关联的元数据。
作为参考,我使用的是 Neo4j 3.0.3、py2neo 和 Bolt。 Python 代码块的形式为,
for chunk in chunker(relationships): # 1,000 relationships per chunk
with graph.begin() as tx:
statement = """
UNWIND {rows} AS row
MATCH (s:Entity) WHERE s.uuid = row.source
MATCH (t:Entity) WHERE t.uuid = row.target
MATCH (s)-[r:CONSUMED]->(t)
SET r += row.properties
"""
rows = []
for rel in chunk:
rows.append({
'properties': dict(rel),
'source': rel.start_node()['uuid'],
'target': rel.end_node()['uuid'],
})
tx.run(statement, rows=rows)
试试这个查询:
UNWIND {rows} AS row
WITH row.source as source, row.target as target, row
MATCH (s:Entity {uuid:source})
USING INDEX s:Entity(uuid)
WITH * WHERE true
MATCH (t:Entity {uuid:target})
USING INDEX t:Entity(uuid)
MATCH (s)-[r:CONSUMED]->(t)
SET r += row.properties;
它使用查询计划中显示的 index hints to force an index lookup for both Entity
nodes and then an Expand(Into)
operator which should be more performant than the Expand(All)
和 Filter
运算符。
@william-lyon 我想知道我是否需要 WITH * WHERE true
子句?我问的原因是DB命中数从4增加到8,即
PROFILE
MATCH (s:Entity {uuid:row.source})
USING INDEX s:Entity(uuid)
MATCH (t:Entity {uuid:row.target})
USING INDEX t:Entity(uuid)
MATCH (s)-[r:CONSUMED]->(t)
returns
而
PROFILE
MATCH (s:Entity {uuid:row.source})
USING INDEX s:Entity(uuid)
WITH * WHERE true
MATCH (t:Entity {uuid:row.target})
USING INDEX t:Entity(uuid)
MATCH (s)-[r:CONSUMED]->(t)
returns
请注意,使用索引提示可将数据库命中数从 6 减少到 4。对于上下文,我们有多个节点标签(和索引),尽管每个节点都有 :Entity
标签。