我最近一直在玩neo4j/cypher来存储对象。 我正在使用带有neo4j的netcore。驱动程序 v1.5。
一切都很好,直到我需要更高的性能,因为我需要在运行时存储数百万个节点/关系。
在花了一些时间挖掘其他一些问题和文章之后,我决定重构我以"幼稚"的方式调用neo4j时所拥有的东西。(每次创建 1 个请求)
我现在通过以下方式使用查询参数来减少查询数量:
用于节点创建
MERGE (model:EngineeringModel {id: $engineeringModelId})
WITH model
UNWIND $nodes AS node
CREATE (thing:Label1:Label2:LabelN)<-[:Model]-(model)
SET thing = node
其中$engineeringModelId是一个字符串,$nodes对应于每个节点属性的对象数组。
用于创建关系
MATCH (model:EngineeringModel {id: $engineeringModelId}) USING INDEX
model:EngineeringModel(id)
UNWIND $relationships AS relationship
MATCH (s:Label1 {id: relationship.source}) USING INDEX s:Label1(id)
UNWIND relationship.sourceRelationships AS sourceRelationship
UNWIND sourceRelationship.targets AS target
MATCH (t:IIdentifiable {id: target}) USING INDEX t:IIdentifiable(id)
CALL apoc.create.relationship(s, sourceRelationship.type, {{}}, t) YIELD rel
RETURN rel
哪里 $engineeringModelId是一个字符串, $relationships 是一个对象数组,其中包含源节点及其所有目标/关系类型。
我设法在 5 分钟内使用事务函数保存了大约 1M 个节点/关系,并将请求拆分为多达 20000 个对象。(拆分是在 C# 代码中完成的)
using (var session = Driver.Session())
{
session.WriteTransaction(tx => tx.Run(request, params));
}
这样做的问题是,在每 20000 个节点/关系执行一次提交的意义上,它不是"事务安全的"。
通过使用 4.3.2.3 中所述的显式事务 neo4j 文档 我无法在合理的时间内执行提交(我从未设法保存)。
this.driverLifeCycle.BeginTransaction(); // connect and start a new transaction
this.DriverLifeCycle.Transaction.Run(cqlQuery.ToString(), parameters); xN times
this.driverLifeCycle.CommitTransaction(); // commit the transaction
以下是我为 docker 容器提供的配置:
NEO4J_dbms_memory_pagecache_size=2G
NEO4J_dbms_memory_heap_maxSize=2G
NEO4J_dbms_memory_heap_initial__size=2G
有人知道如何使此显式事务正常工作吗?
非常感谢大家的阅读!
因此,经过一些调查以及neo4j社区对neo4j-slack的一些输入,似乎不可能直接在我的neo4j版本(v 3.2.5)中使用.netcore的驱动程序v1.5实现我想要的东西,并且需要"手动"处理事务的解决方法。
解决方法基本上如下:
- 为要在当前事务中创建的所有节点添加一个额外的TMP标签,其中包含在 POST 请求上生成的transactionId属性,
- 在事务期间使用相同的事务 ID 创建的关系上添加额外的属性事务 ID。
我还每几千个(在我的例子中是 10k)提交一次节点/关系,以便 neo4j 可以处理它并确保包含这些额外的标签和属性。
在操作结束时:
-
成功后,我删除了应用于节点和关系的额外标签和属性
MATCH (tmpNode:TMP { transactionId: $transactionId }) USING INDEX tmpNode:TMP(transactionId) REMOVE tmpNode:TMP REMOVE tmpNode.transactionId MATCH ()-[r { transactionId: $transactionId }]->() REMOVE r.transactionId
-
如果出现问题,我会删除创建的所有节点和关系。
MATCH (tmpNode:TMP { transactionId: $transactionId }) USING INDEX tmpNode:TMP(transactionId) DETACH DELETE tmpNode MATCH ()-[tmpRel { transactionId: $transactionId }]->() DELETE tmpRel
这两个操作都可以简化为一个请求而不是两个请求,但考虑到数据量,这样做更有效。
在我的本地机器上,我设法在大约 10 分钟内运行它,这仍然不是很好,但至少是可行的。