从 Neo4j 加载 9M 行并将其写入 CSV 会抛出内存异常

Loading 9M rows from Neo4j and writing it into CSV throws out of memory exception

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

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

当我 "Explain" 查询时,这是我得到的结果:

显示估计结果在9M左右。我的问题是:

1) 得到回复需要很长时间。从 neo4j-shell 开始需要 38 分钟!这是正常的吗?顺便说一句,我有所有 架构索引 并且它们都在线。

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

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

P.S 我正在使用 Neo4j 社区版 Version:3.0.1,这些是我的系统信息:

这些是我的服务器配置。

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

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

您的 Neo4j 3.0.1 配置设置不正确

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

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

正如您所见,它将创建 8+ 百万行。

你可能对这个查询更有运气:

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

说实话,return 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(*)

为了减少 returned 结果的数量,我可能会做的另一件事是尝试聚合。

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(*)

感谢 Vince 和 Michael 的评论,我找到了解决方案! 做了一些实验后,很明显服务器响应时间实际上很好! 900万条数据1.5分钟!正如 Vince 提到的,问题出在 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(false);”和 "st.setFetchSize(50)"。谢谢大家!