我在Neo4J 2.1.2中使用Cypher的LOAD CSV
语法。到目前为止,它是对以前版本中所需的更多手动ETL过程的巨大改进。但是我在一个案例中遇到了一些不符合我期望的行为,我想知道我是否遗漏了什么。
正在使用的密码查询是:
USING PERIODIC COMMIT 500
LOAD CSV FROM 'file:///Users/James/Desktop/import/dependency_sets_short.csv' AS row
MATCH (s:Sense {uid: toInt(row[4])})
MERGE (ds:DependencySet {label: row[2]}) ON CREATE SET ds.optional=(row[3] = 't')
CREATE (s)-[:has]->(ds)
以下是CSV的几行:
227303,1,TO-PURPOSE-NOMINAL,t,73830
334471,1,AT-LOCATION,t,92048
334470,1,AT-TIME,t,92048
334469,1,ON-LOCATION,t,92048
227302,1,TO-PURPOSE-INFINITIVE,t,73830
116008,1,TO-LOCATION,t,68204
116007,1,IN-LOCATION,t,68204
227301,1,TO-LOCATION,t,73830
334468,1,ON-DATE,t,92048
116006,1,AT-LOCATION,t,68204
334467,1,WITH-ASSOCIATE,t,92048
基本上,我根据它的ID值(第五列)匹配Sense
节点(先前已导入)。然后我正在进行合并以获得DependencySet
节点(如果存在)或创建它。最后,我在has
节点和Sense
节点之间创建DependencySet
边缘。到目前为止一切顺利,这一切都按预期工作。随着CSV大小的增长,性能会让人感到困惑。
CSV Lines Time (msec)
------------------------------
500 480
1000 717
2000 1110
5000 1521
10000 2111
50000 4794
100000 5907
200000 12302
300000 35494
400000 Java heap space error
我的期望是增长会或多或少是线性的,特别是当我按照manual的建议每500行提交时,但它实际上更接近多项式:
更糟糕的是,在300k到400k行之间,它会遇到Java堆空间错误。根据之前进口的趋势,我预计400k的进口需要一分多钟。相反,它会在遇到堆空间错误之前搅拌大约5-7分钟。看起来我可以将这个文件分成300,000行的块,但这不是“使用周期性委托”应该做的,或多或少?我想我也可以为Neo4J提供更多的内存,但同样,我不清楚为什么在这种情况下我应该这么做。
另外,要明确的是,Sense.uid
和DependencySet.label
上的查找都已编入索引,因此对这些查找的查找惩罚应该非常小。这是架构的一个片段:
Indexes
ON :DependencySet(label) ONLINE (for uniqueness constraint)
ON :Sense(uid) ONLINE (for uniqueness constraint)
对于替代方法的任何解释或想法都将受到赞赏。
编辑:问题肯定出现在查询的MATCH和/或CREATE部分。如果我从Cypher查询中删除第3行和第5行,它就会正常运行。
答案 0 :(得分:4)
我认为您在运行此Sense
导入之前已经创建了所有LOAD CSV
标记的节点。我的想法是,当您将带有标签Sense
的节点匹配到内存中并通过DependencySet
创建从Sense
到CREATE (s)-[:HAS]->(ds)
节点的关系时,您正在增加利用可用堆。
另一种可能性是需要增加内存映射设置中关系存储的大小。在您的方案中,Sense
节点看起来与图中的其他节点具有高度连接。发生这种情况时,这些节点的关系存储需要更多内存。最终当你达到400k节点时,堆最大化了。到那时为止,它需要进行更多垃圾收集并从磁盘读取。
Michael Hunger撰写了一篇关于内存映射设置的优秀博文,以实现快速LOAD CSV
性能。见这里:http://jexp.de/blog/2014/06/load-csv-into-neo4j-quickly-and-successfully/
这应该可以解决您的问题。我认为您的查询没有任何问题。
答案 1 :(得分:1)
我相信这一行
MATCH (s:Sense {uid: toInt(row[4])})
制定时间范例。在图表的x行中的200 000左右,您不再拥有内存中的所有Sense
节点,但其中一些节点必须缓存到磁盘。因此,所有时间的增加只是将数据从缓存重新加载到内存中,反之亦然(否则,如果保存在内存中,它仍然是线性的)。
也许如果您可以发布服务器内存设置,我们可以深入挖掘。
关于java堆错误的问题请参考Kenny的回答