如何解释Cypher的LOAD CSV子句的性能?

时间:2014-06-26 15:08:50

标签: neo4j cypher

我在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行提交时,但它实际上更接近多项式: Graph of load time by number of CSV lines

更糟糕的是,在300k到400k行之间,它会遇到Java堆空间错误。根据之前进口的趋势,我预计400k的进口需要一分多钟。相反,它会在遇到堆空间错误之前搅拌大约5-7分钟。看起来我可以将这个文件分成300,000行的块,但这不是“使用周期性委托”应该做的,或多或少?我想我也可以为Neo4J提供更多的内存,但同样,我不清楚为什么在这种情况下我应该这么做。

另外,要明确的是,Sense.uidDependencySet.label上的查找都已编入索引,因此对这些查找的查找惩罚应该非常小。这是架构的一个片段:

Indexes
  ON :DependencySet(label) ONLINE (for uniqueness constraint)
  ON :Sense(uid)           ONLINE (for uniqueness constraint)

对于替代方法的任何解释或想法都将受到赞赏。

编辑:问题肯定出现在查询的MATCH和/或CREATE部分。如果我从Cypher查询中删除第3行和第5行,它就会正常运行。

2 个答案:

答案 0 :(得分:4)

我认为您在运行此Sense导入之前已经创建了所有LOAD CSV标记的节点。我的想法是,当您将带有标签Sense的节点匹配到内存中并通过DependencySet创建从SenseCREATE (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的回答