【问题标题】:Neo4J Traversal Running out of memoryNeo4J 遍历内存不足
【发布时间】:2014-04-14 20:04:04
【问题描述】:

我有一个包含大约 1.2 亿个节点的 neo4j 数据库。我正在使用遍历框架来遍历我的图表并计算某些节点的出现。这就像一个魅力。不幸的是,在我的整个数据集上运行我的代码时,我的内存不足。

我已经为 Java VM 分配了 4gb,我想我提交了我的事务(使用 try-with-resources 语句中的 tx.success),但我仍然很快填满了我的堆。

您可以在下面找到我的代码: 首先我生成了大约 40 个版本(这些是根节点)。然后对于其中的每一个,我都会查找所有相邻的子节点。对于这些子(文件)中的每一个,我会检查整个子树中是否存在某个节点。

我的理解是使用

try(Transaction tx){
 }

自动关闭了我的交易,但我的堆还是满的。这使得我的查询从第二次或第三次传递版本开始运行缓慢,最终崩溃。我是不是误会了什么?或者还有什么我可以做的吗?

    Collection<Node> versions;
    Collection<Node> files;
    Collection<Node> nodes;
    try ( Transaction ignored = db.beginTx() )
    {
        versions = IteratorUtil.asCollection(db.traversalDescription().breadthFirst().relationships(ProjectRelations.HASVERSION, Direction.OUTGOING).evaluator(Evaluators.toDepth(1)).evaluator(Evaluators.excludeStartPosition()).traverse(db.getNodeById(0)).nodes());
        ignored.success();
    }

    for(Node v : versions){
        int fors = 0;
        test = 0;

        try( Transaction tx = db.beginTx()){

            files = IteratorUtil.asCollection(db.traversalDescription().breadthFirst().relationships(ProjectRelations.FILES, Direction.OUTGOING).evaluator(Evaluators.excludeStartPosition()).traverse(v).nodes());

            tx.success();
        }

        for( Node f : files ) {

            try (Transaction t = db.beginTx()){
                int i = 0;
                for(Node node : db.traversalDescription().depthFirst().relationships(RelTypes.ISPARENTOF, Direction.OUTGOING).evaluator(Evaluators.excludeStartPosition()).evaluator(e).traverse(f).nodes()){
                     //do some stuff
                }
                t.success();
            }   
        }

        files.clear();


    }
    versions.clear();

更新:

我用迭代器替换了所有内容,例如:

try( 
                Transaction tx = db.beginTx(); 
                ResourceIterator<Node> files = db.traversalDescription().breadthFirst().relationships(ProjectRelations.FILES, Direction.OUTGOING).evaluator(Evaluators.excludeStartPosition()).traverse(v).nodes().iterator();
            ){



            int idx = 0;
            forloops = 0;
            long start = System.nanoTime();

            while( files.hasNext() ) {

                Node f = files.next();


                try (Transaction t = db.beginTx();
                        ResourceIterator<Node> blah = db.traversalDescription().depthFirst().relationships(RelTypes.ISPARENTOF, Direction.OUTGOING).evaluator(Evaluators.excludeStartPosition()).evaluator(e).traverse(f).nodes().iterator();
                        ){
                    int i = 0;

                    while(blah.hasNext()){
                        Node tempNode = blah.next();

                    }
                    blah.close();

                }

            }
            files.close();
        }


    }

问题是事务将所有内容都保存在内存中,直到我用尽迭代器或 close() 它

编辑 2:

我对所有事情都使用了迭代器,使用深度优先遍历。我还将可用堆内存从 4 GB 更改为 1024mb。现在它似乎正在运行(虽然我不确定它是否会完全完成),尽管 非常 很慢。它运行到大约 980mb,但还没有跨越这个门槛。由于我的堆在整个时间里都满了,所以确实有一个巨大的减速。有什么想法可以改进吗?或者这是我能得到的最好的?

    try(Transaction tx = db.beginTx()){
    versions = IteratorUtil.asCollection(db
                .traversalDescription()
                .depthFirst()
                .relationships(ProjectRelations.HASVERSION,
                        Direction.OUTGOING)
                .evaluator(Evaluators.toDepth(1))
                .evaluator(Evaluators.excludeStartPosition())
                .traverse(root));

    }
    int mb = 1024 * 1024;
    Runtime runtime = Runtime.getRuntime();

    ResourceIterator<Node> files = null;

    try(Transaction tx = db.beginTx()){
        int idx = 0;
        for(Relationship rel : root.getRelationships(ProjectRelations.HASVERSION, Direction.OUTGOING)){
            idx++;
            System.out.println(idx);
            Node v = rel.getEndNode();
            files = db.traversalDescription().depthFirst().relationships(ProjectRelations.FILES, Direction.OUTGOING).evaluator(Evaluators.excludeStartPosition()).uniqueness(Uniqueness.NONE).traverse(v).nodes().iterator();
            long start = System.nanoTime();
            while(files.hasNext()){

                Node f = files.next();

                ResourceIterator<Node> node = db.traversalDescription().depthFirst().relationships(RelTypes.ISPARENTOF, Direction.OUTGOING).evaluator(Evaluators.excludeStartPosition()).evaluator(e).traverse(f).nodes().iterator();
                while(node.hasNext()){
                    node.next();
                }

            }
            System.out.println("Used Memory:"
                    + (runtime.totalMemory() - runtime.freeMemory()) / mb);
            System.out
                    .println("Total Memory:" + runtime.totalMemory() / mb);


            files.close();
        }

    }

    db.shutdown();

抛出的异常:

Exception in thread "GC-Monitor" Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)
at java.lang.AbstractStringBuilder.expandCapacity(Unknown Source)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(Unknown Source)
at java.lang.AbstractStringBuilder.append(Unknown Source)
at java.lang.StringBuilder.append(Unknown Source)
at ch.qos.logback.core.pattern.FormattingConverter.write(FormattingConverter.java:40)
at ch.qos.logback.core.pattern.PatternLayoutBase.writeLoopOnConverters(PatternLayoutBase.java:119)
at ch.qos.logback.classic.PatternLayout.doLayout(PatternLayout.java:168)
at ch.qos.logback.classic.PatternLayout.doLayout(PatternLayout.java:59)
at ch.qos.logback.core.encoder.LayoutWrappingEncoder.doEncode(LayoutWrappingEncoder.java:134)
at ch.qos.logback.core.OutputStreamAppender.writeOut(OutputStreamAppender.java:188)
at ch.qos.logback.core.FileAppender.writeOut(FileAppender.java:206)
at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:212)
at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:103)
at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:88)
at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:48)
at ch.qos.logback.classic.Logger.appendLoopOnAppenders(Logger.java:272)
at ch.qos.logback.classic.Logger.callAppenders(Logger.java:259)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:441)
at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:395)
at ch.qos.logback.classic.Logger.warn(Logger.java:708)
at org.neo4j.kernel.logging.LogbackService$Slf4jToStringLoggerAdapter.warn(LogbackService.java:240)
at org.neo4j.kernel.impl.cache.MeasureDoNothing.run(MeasureDoNothing.java:84)

java.lang.OutOfMemoryError: Java heap space
at java.util.ArrayList.<init>(Unknown Source)
at org.neo4j.kernel.impl.core.RelationshipLoader.getMoreRelationships(RelationshipLoader.java:55)
at org.neo4j.kernel.impl.core.NodeManager.getMoreRelationships(NodeManager.java:779)
at org.neo4j.kernel.impl.core.NodeImpl.loadMoreRelationshipsFromNodeManager(NodeImpl.java:577)
at org.neo4j.kernel.impl.core.NodeImpl.getMoreRelationships(NodeImpl.java:466)
at org.neo4j.kernel.impl.core.NodeImpl.loadInitialRelationships(NodeImpl.java:394)
at org.neo4j.kernel.impl.core.NodeImpl.ensureRelationshipMapNotNull(NodeImpl.java:372)
at org.neo4j.kernel.impl.core.NodeImpl.getAllRelationshipsOfType(NodeImpl.java:219)
at org.neo4j.kernel.impl.core.NodeImpl.getRelationships(NodeImpl.java:325)
at org.neo4j.kernel.impl.core.NodeProxy.getRelationships(NodeProxy.java:154)
at org.neo4j.kernel.StandardExpander$RegularExpander.doExpand(StandardExpander.java:583)
at org.neo4j.kernel.StandardExpander$RelationshipExpansion.iterator(StandardExpander.java:195)
at org.neo4j.kernel.impl.traversal.TraversalBranchImpl.expandRelationshipsWithoutChecks(TraversalBranchImpl.java:115)
at org.neo4j.kernel.impl.traversal.TraversalBranchImpl.expandRelationships(TraversalBranchImpl.java:104)
at org.neo4j.kernel.impl.traversal.TraversalBranchImpl.initialize(TraversalBranchImpl.java:131)
at org.neo4j.kernel.impl.traversal.TraversalBranchImpl.next(TraversalBranchImpl.java:151)
at org.neo4j.graphdb.traversal.PreorderDepthFirstSelector.next(PreorderDepthFirstSelector.java:49)
at org.neo4j.kernel.impl.traversal.MonoDirectionalTraverserIterator.fetchNextOrNull(MonoDirectionalTraverserIterator.java:68)
at org.neo4j.kernel.impl.traversal.MonoDirectionalTraverserIterator.fetchNextOrNull(MonoDirectionalTraverserIterator.java:35)
at org.neo4j.helpers.collection.PrefetchingIterator.hasNext(PrefetchingIterator.java:55)
at org.neo4j.kernel.impl.traversal.DefaultTraverser$ResourcePathIterableWrapper$1.fetchNextOrNull(DefaultTraverser.java:140)
at org.neo4j.helpers.collection.PrefetchingIterator.hasNext(PrefetchingIterator.java:55)
at main.QueryExecutor.main(QueryExecutor.java:173)

【问题讨论】:

  • 您确定您的版本集合没有消耗堆吗?无论如何,你在那里放了多少东西?该系列会持续多久?看起来您正在消耗整个第一次遍历并将其存储在内存中。将第二个事务作为嵌套事务进行可能更好,而不是将所有这些内容存储在内存中。
  • 版本是 40 个节点,所以一点也不大。我会尝试循环遍历迭代器。
  • BFS 必须将整个 trackback 保存在内存中,尝试使用 DFS
  • 可以分享一下OOM异常吗?
  • 如果您不更改数据,事务不会将任何内容放入内存中。

标签: java neo4j


【解决方案1】:

当您使用 IteratorUtil.asCollection() 执行第二次遍历时,您似乎正在急切地使用整个迭代器。我不确定在这种情况下会产生多少节点,但如果它们很多(即数百万),很可能会导致内存不足的问题。

【讨论】:

  • 我去看看,应该不会有那么多节点,但值得一试。问题是 Neo4j 只是将嵌套事务合并为一个(如果我理解正确的话)。所以我必须在我的第二次中嵌套我的第三次遍历,这会消耗更多的内存? Neo4J 将整个事务保存在内存中直到关闭(在这种情况下,到达 try 块的末尾)
【解决方案2】:

我通过将cache_type 选项设置为none 解决了我的问题。它不会耗尽内存并在大约一个小时内完成。

【讨论】:

    猜你喜欢
    • 2015-08-26
    • 2012-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-10
    • 1970-01-01
    • 2016-01-01
    • 1970-01-01
    相关资源
    最近更新 更多