我有一个带有600k顶点和950k边的图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或其他图形提供程序会更快吗?
发布于 2021-06-19 11:05:38
不太可能有比TinkerGraph更快的图形,因为TinkerGraph是一个纯内存实现。您可能会发现使用该内存的效率更高,比如OverflowDB,它最初是从TinkerGraph分叉而来的,但我不知道它是否会使这个特定的查询更快。
TinkerGraph,以及我所知道的任何一个图表,都有一个经过过滤的“大容量下降”操作。
这里的"not“样式的全局查询简单、昂贵,因为您必须触摸图形的很大一部分。当然,我感到有点惊讶的是,TinkerGraph花了这么长的时间才得到一个小于一百万条边的图。当你做你的个人资料时,你没有提到你是否经历了大量的GC。也许这是个问题?如果是这样的话,我会尝试调整您的JVM内存配置--也许您只需要一个更大的-Xmx值或类似这样的简单值。
从查询的角度来看,您可以尝试倒转遍历的not()部分,以积极地查找要删除的内容。它可能导致读取的查询不那么简洁,但可能会加快速度,但另一方面,您仍在尝试删除50%的数据,因此成本可能不只是查找要删除的顶点。
另一个想法是尝试并行化drop()。您可能会遇到并发错误,因此需要重试策略,但是可以考虑使用Iterator of g.V().hasLabel(LABEL_INTERMEDIATE_COLUMN).not(inE(EDGE_DEPEND)),然后将对每个(或多个) Vertex.remove()的调用委托给一个单独的工作线程。
发布于 2021-06-21 21:34:04
基于公认的答案,一个简单的并行化改进得足够多,使这个操作不再是最关键的时间。
供日后参考:
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也可能太多(总是有一个或多个线程处于等待状态)--尽管我确实得到了一个具有3个线程运行的转储!
https://stackoverflow.com/questions/68042799
复制相似问题