【问题标题】:Improve performance removing TinkerGraph vertices提高删除 TinkerGraph 顶点的性能
【发布时间】:2021-09-03 15:24:43
【问题描述】:

我有一个图 g600k 顶点和 950k 边。经过一些处理,我需要用这个查询清理大约350k+ 顶点:

g.V().hasLabel(LABEL_INTERMEDIATE_COLUMN).not(inE(EDGE_DEPEND)).drop().iterate();

即使我排除了没有“依赖”边的顶点,它们仍然与其他边相连。

使用 Java,tinkerpop/tinkergraph 3.4.6。

目前删除所有这些顶点大约需要 45 分钟

我做了一个 java 分析,结果显示 73% 的时间花在 TinkerVertex.remove 方法上,其余的时间花在 ExpandableStepIterator.next

有类似“散装”的东西吗? JanusGraph 或其他图形提供程序会更快吗?

【问题讨论】:

    标签: gremlin tinkerpop tinkerpop3 tinkergraph


    【解决方案1】:

    根据公认的答案,一个简单的并行化得到了足够的改进,该操作不再是最关键的时间

    为了将来参考,这个:

    g.V().hasLabel(LABEL_INTERMEDIATE_COLUMN).not(inE(EDGE_DEPEND)).drop().iterate(); 
    

    现在是这样的:

    ExecutorService executor = Executors.newFixedThreadPool(4);
    int iterator = 0;
    final int batchsize = 10000;
    Long count = g.V().hasLabel(LABEL_INTERMEDIATE_COLUMN).not(inE(EDGE_DEPEND)).count().next();
    List<Callable<Object>> callableList = new ArrayList<Callable<Object>>();
    
    // splitting current set into tasks to be executed in para
    while (iterator * batchsize < count) {
        final Set<Object> vSet =  g.V().hasLabel(LABEL_INTERMEDIATE_COLUMN).not(inE(EDGE_DEPEND)).skip(iterator * batchsize).limit(batchsize).id().toSet();
        callableList.add(() -> g.V(vSet).drop().iterate());
        iterator++;
    }
    List<Future<Object>> results = executor.invokeAll(callableList);
    

    经过一些测试,我决定将迭代保留在一个线程中。这样分布式任务就真正相互独立了(例如:一个任务完成不会影响其他任务查询)。

    请记住,实际删除仍然是单线程,因为顶点节点映射修改在并发访问锁之后。

    效果是增加线程数不会得到更好的结果(个人试过8个)。并且基于一些线程转储,即使是 4 个也可能太多(总是有 1 个或更多线程处于等待状态) - 尽管我确实得到了一个运行 3 个线程的转储!

    【讨论】:

      【解决方案2】:

      不太可能有比 TinkerGraph 快得多的图形,因为 TinkerGraph 是纯内存实现。您可能会发现使用该内存的效率更高,例如 OverflowDB,它最初是从 TinkerGraph 派生的,但我不知道它会使这个特定的查询更快。

      TinkerGraph 以及我所知道的任何图表都具有过滤的“批量删除”操作。

      这里的“not”样式全局查询很简单,因为您必须触摸图表的大部分。当然,我有点惊讶 TinkerGraph 花了这么长时间才得到一个边数少于 100 万的图。你没有提到你在做你的个人资料时是否经历了很多 GC。也许这是一个问题?如果是这样,我会尝试调整您的 JVM 内存配置 - 也许您只需要更大的 -Xmx 值或类似的简单值。

      从查询的角度来看,您可以尝试反转遍历的not() 部分,以肯定地找到您想要删除的内容。它可能会导致读取的查询不太简洁,但可能会加快速度,但另一方面,您仍在尝试删除 50% 的数据,因此成本可能不仅仅在于找到要摆脱的顶点。

      另一个想法是尝试并行化drop()。您可能会遇到并发错误,因此您可能需要重试策略,但您可以考虑采用 Iteratorg.V().hasLabel(LABEL_INTERMEDIATE_COLUMN).not(inE(EDGE_DEPEND)),然后将对每个(或批次)Vertex.remove() 的调用委托给单独的工作线程。

      【讨论】:

      • 内存分配很好,更改提供程序似乎太麻烦了,改进的可能性很小,查询重构相同(75% 的时间用于实际删除)。这让我不得不并行化它。从 45+- 分钟减少到 8+- 分钟! (查询也发生了一些变化)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多