从Neo4j加载9M行并将其写入CSV会导致内存不足异常

时间:2016-11-16 13:41:45

标签: neo4j spring-data-neo4j spring-data-neo4j-4 neo4j-ogm

我有一个大图模型,我需要将以下查询的结果写入csv。

Match (u:USER)-[r:PURCHASED]->(o:ORDER)-[h:HAS]->(i:ITEM) return u.id as userId,i.product_number as itemId

当我"解释"查询,这是我得到的结果: enter image description here

它显示估计结果约为9M。我的问题是:

1)获得回复需要很多时间。从neo4j-shell开始需要38分钟!这是正常的吗?顺便说一下,我有模式索引,它们都在线。

2)当我使用SpringDataNeo4j获取结果时,它会抛出一个" java.lang.OutOfMemoryError:超出GC开销限制"错误,当SDN尝试将加载的数据转换为@QueryResult对象时发生这种情况。

我尝试以各种方式优化查询,但没有改变!我的印象是我做错了什么。有谁知道如何解决这个问题?我应该去批量读/写吗?

P.S我正在使用Neo4j社区版本:3.0.1,这些是我的sysinfos: enter image description here

这些是我的服务器配置。

dbms.jvm.additional=-Dunsupported.dbms.udc.source=tarball
use_memory_mapped_buffers=true
neostore.nodestore.db.mapped_memory=3G
neostore.relationshipstore.db.mapped_memory=4G
neostore.propertystore.db.mapped_memory=3G
neostore.propertystore.db.strings.mapped_memory=1000M
neostore.propertystore.db.index.keys.mapped_memory=500M
neostore.propertystore.db.index.mapped_memory=500M

3 个答案:

答案 0 :(得分:6)

虽然Neo4j会在匹配它们时将结果传输给您,但是当您使用SDN时,它必须将输出收集到单个@QueryResult对象中。为避免出现OOM问题,您需要确保应用程序有足够的堆内存可用于加载所有9m响应,或使用neo4j-shell,或使用专用流媒体接口,例如https://www.npmjs.com/package/cypher-stream(告诫者:我还没试过这个,但看起来应该这样做了)

答案 1 :(得分:3)

您的配置设置不适用于Neo4j 3.0.1

你必须在conf / neo4j-wrapper.conf中设置堆,例如8G

和conf / neo4j.conf中的页面缓存(查看您的商店,您只需要2G用于页面缓存)。

另外,正如您所看到的,它将创建超过800万行。

您可能对此查询有更多好运:

Match (u:USER)-[:PURCHASED]->(:ORDER)-[:HAS]->(i:ITEM) 
with distinct u,i
return u.id as userId,i.product_number as itemId

说实话,将8M行返回neoj-shell也没有意义。 如果您想衡量它,请将RETURN替换为WITH并添加RETURN count(*)

Match (u:USER)-[r:PURCHASED]->(o:ORDER)-[h:HAS]->(i:ITEM) 
with distinct u,i
WITH u.id as userId,i.product_number as itemId
RETURN count(*)

另一个优化可以通过项目和用户进行,并在中间执行哈希联接以获得全局查询,如下所示:

Match (u:USER)-[:PURCHASED]->(o:ORDER)-[:HAS]->(i:ITEM) 
USING JOIN ON o
with distinct u,i
WITH u.id as userId,i.product_number as itemId
RETURN count(*)

我可能做的另一件事就是减少返回结果的数量是尝试聚合。

Match (u:USER)-[:PURCHASED]->(o:ORDER)-[:HAS]->(i:ITEM)
with distinct u,i
WITH u, collect(distinct i) as products
WITH u.id as userId,[i in products | i.product_number] as items
RETURN count(*)

答案 2 :(得分:2)

感谢Vince和Michael的评论,我找到了解决方案! 做了一些实验后,很清楚服务器响应时间实际上是好的! 900万分数为1.5分钟!文斯提到的问题在于SDN!当SDN尝试将数据转换为@QueryResult对象时,就会发生OOM。为我们的应用程序增加堆内存不是永久解决方案,因为我们将来会有更多行!所以我们决定使用 neo4j-jdbc-driver 进行大数据查询...&它像喷气机一样工作!以下是我们使用的代码示例:

Class.forName("org.neo4j.jdbc.Driver");
    try (Connection con = DriverManager.getConnection("jdbc:neo4j:bolt://HOST:PORT", "USER", "PASSWORD")) {

        // Querying
        String query = "match (u:USER)-[r:PURCHASED]->(o:ORDER)-[h:HAS]->(i:ITEM) return u.id as userId,i.product_number as itemId";
        con.setAutoCommit(false); // important for large dataset
        Statement st = con.createStatement();
        st.setFetchSize(50);// important for large dataset

            try (ResultSet rs = st.executeQuery(query)) {
                while (rs.next()) {
                    writer.write(rs.getInt("userId") + ","+rs.getInt("itemId"));
                    writer.newLine();
                }

            }

        st.setFetchSize(0);
        writer.close();
        st.close();

    }

请确保使用" con.setAutoCommit(假); "和" st.setFetchSize(50)"如果您知道要加载大型数据集。谢谢大家!